nginx_lua 扩展让 nginx 拥有可编程能力
2016-01-26 08:56
561 查看
公司使用 lighttpd 的比较多, 主要是接入层的一些工作,而且增加了一些很多自己的模块防火墙等等.
后来nginx开始流行起来因为 lighttpd 和 nginx整体是实现方式比较类似(个人感觉nginx 借鉴了 lighttpd的实
现方式),都使用了多进程异步非阻塞处理请求I/O和timer,对于静态文件服务使用sendfile系统调用. 作为
静态文件server 和接入层来说 lighttpd 已经足够的快,所以用lighttpd 和 nginx没太大区别. 简单来说nginx
相对于lighttpd 没有 质的提高, 所以公司推广nginx 的动力不是太大.
nginx_lua 模块的推出使得 nginx 和 lighttpd 不在一个水平线上了. nginx_lua大大降低了nginx moudle
开发的门槛,使用lua 语言可以替代以前使用c开发nginx moudle的很多场景. 可以很方便的给nginx 增加功能
这点lighttpd 很难做到.
关于nginx_lua的介绍可以看看作者的一个演讲记录: 由Lua 粘合的Nginx生态环境 ,本文主要介绍lua
socket I/O特点。 根据nginx 工作方式的特点每Nginx工作进程使用一个Lua VM,工作进程内所有协程共
享VM. 每一个外部请求都由一个新的Lua协程处理, 协程之间数据隔离. 当Lua代码调用I/O操作接口时,若
该操作无法立刻完成(例如 recv 会引起阻塞)协程会保存当前状态, 由Nginx 继续处理其他请求, 相关数据I/O
操作完成时resume相关协程并继续运行。
上述例子中的 socket:receive()要等待对方数据返回的, nginx_lua 模块适配了 nginx.socket I/O操作,
nginx.socket的 I/O 操作都不会阻塞当前工作进程, nginx.socket 可以使用同步的方式实现异步I/O, 工作
方式上面说了一些,下面简单来分析一下:
1. 新的http请求执行到 lua 代码时会创建一个新的lua 协程
2. 当该lua 协程调用sock:receive()等函数时, 会有nginx.socket 来接管 I/O操作,nginx.socket 会yield当前
协程,注册 I/O的回调到nginx事件循环中,继续Nginx的其他处理
3. 当收到数据时,Nginx获得到该事件调用回调,唤醒之前的协程继续处理
我们拿最简单的ngx.sleep 流程看一下nginx 的内部处理, 下面lua 代码针对该请求sleep 1秒钟, 并返回结果
Nginx lua 实现:
nginx_lua 还有其他很多强大的功能,像是一把瑞士军刀大大增强了nignx的扩展性和灵活性,应用场景
很多可以参照下京东的应用:http://jinnianshilongnian.iteye.com/category/333854
后来nginx开始流行起来因为 lighttpd 和 nginx整体是实现方式比较类似(个人感觉nginx 借鉴了 lighttpd的实
现方式),都使用了多进程异步非阻塞处理请求I/O和timer,对于静态文件服务使用sendfile系统调用. 作为
静态文件server 和接入层来说 lighttpd 已经足够的快,所以用lighttpd 和 nginx没太大区别. 简单来说nginx
相对于lighttpd 没有 质的提高, 所以公司推广nginx 的动力不是太大.
nginx_lua 模块的推出使得 nginx 和 lighttpd 不在一个水平线上了. nginx_lua大大降低了nginx moudle
开发的门槛,使用lua 语言可以替代以前使用c开发nginx moudle的很多场景. 可以很方便的给nginx 增加功能
这点lighttpd 很难做到.
关于nginx_lua的介绍可以看看作者的一个演讲记录: 由Lua 粘合的Nginx生态环境 ,本文主要介绍lua
socket I/O特点。 根据nginx 工作方式的特点每Nginx工作进程使用一个Lua VM,工作进程内所有协程共
享VM. 每一个外部请求都由一个新的Lua协程处理, 协程之间数据隔离. 当Lua代码调用I/O操作接口时,若
该操作无法立刻完成(例如 recv 会引起阻塞)协程会保存当前状态, 由Nginx 继续处理其他请求, 相关数据I/O
操作完成时resume相关协程并继续运行。
location = /tcptest { content_by_lua ' local sock = ngx.socket.tcp() sock:settimeout(1000) local ok, err = sock:connect("127.0.0.1", 11211) if not ok then ngx.say("failed to connect: ", err) return end local bytes, err = sock:send("flush_all\r\n") if not bytes then ngx.say("failed to send query: ", err) return end local line, err = sock:receive() if not line then ngx.say("failed to receive a line: ", err) return end ngx.say("result: ", line) '; }
上述例子中的 socket:receive()要等待对方数据返回的, nginx_lua 模块适配了 nginx.socket I/O操作,
nginx.socket的 I/O 操作都不会阻塞当前工作进程, nginx.socket 可以使用同步的方式实现异步I/O, 工作
方式上面说了一些,下面简单来分析一下:
1. 新的http请求执行到 lua 代码时会创建一个新的lua 协程
2. 当该lua 协程调用sock:receive()等函数时, 会有nginx.socket 来接管 I/O操作,nginx.socket 会yield当前
协程,注册 I/O的回调到nginx事件循环中,继续Nginx的其他处理
3. 当收到数据时,Nginx获得到该事件调用回调,唤醒之前的协程继续处理
我们拿最简单的ngx.sleep 流程看一下nginx 的内部处理, 下面lua 代码针对该请求sleep 1秒钟, 并返回结果
location /sleep { content_by_lua_block { ngx.sleep(1) ngx.say("1s later..") } }
Nginx lua 实现:
/* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_sleep.h" #include "ngx_http_lua_contentby.h" static int ngx_http_lua_ngx_sleep(lua_State *L); static void ngx_http_lua_sleep_handler(ngx_event_t *ev); static void ngx_http_lua_sleep_cleanup(void *data); static ngx_int_t ngx_http_lua_sleep_resume(ngx_http_request_t *r); static int ngx_http_lua_ngx_sleep(lua_State *L) { int n; ngx_int_t delay; /* in msec */ ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; n = lua_gettop(L); if (n != 1) { return luaL_error(L, "attempt to pass %d arguments, but accepted 1", n); } lua_pushlightuserdata(L, &ngx_http_lua_request_key); lua_rawget(L, LUA_GLOBALSINDEX); r = lua_touserdata(L, -1); lua_pop(L, 1); delay = luaL_checknumber(L, 1) * 1000; if (delay < 0) { return luaL_error(L, "invalid sleep duration \"%d\"", delay); } if (delay == 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua sleep for 0ms"); return 0; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } coctx = ctx->cur_co_ctx; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } coctx->data = r; // 指定 timer超时的回调函数 coctx->sleep.handler = ngx_http_lua_sleep_handler; coctx->sleep.data = coctx; coctx->sleep.log = r->connection->log; dd("adding timer with delay %lu ms, r:%.*s", (unsigned long) delay, (int) r->uri.len, r->uri.data); // 将timer 回调event 加入到 nginx timer 管理中 ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay); coctx->cleanup = ngx_http_lua_sleep_cleanup; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua ready to sleep for %d ms", delay); // 保存当前协程上下文, 并返回到nginx主循环由nginx继续处理其他请求 return lua_yield(L, 0); } // timer expired 触发的回调 void ngx_http_lua_sleep_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_log_ctx_t *log_ctx; ngx_http_lua_co_ctx_t *coctx; coctx = ev->data; r = coctx->data; c = r->connection; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } log_ctx = c->log->data; log_ctx->current_request = r; coctx->cleanup = NULL; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua sleep timer expired: \"%V?%V\"", &r->uri, &r->args); ctx->cur_co_ctx = coctx; if (ctx->entered_content_phase) { //恢复之前的 lua协程,继续处理 (void) ngx_http_lua_sleep_resume(r); } else { ctx->resume_handler = ngx_http_lua_sleep_resume; ngx_http_core_run_phases(r); } ngx_http_run_posted_requests(c); } void ngx_http_lua_inject_sleep_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_sleep); lua_setfield(L, -2, "sleep"); } static void ngx_http_lua_sleep_cleanup(void *data) { ngx_http_lua_co_ctx_t *coctx = data; if (coctx->sleep.timer_set) { dd("cleanup: deleting timer for ngx.sleep"); ngx_del_timer(&coctx->sleep); } } static ngx_int_t ngx_http_lua_sleep_resume(ngx_http_request_t *r) { ngx_connection_t *c; ngx_int_t rc; ngx_http_lua_ctx_t *ctx; ngx_http_lua_main_conf_t *lmcf; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_ERROR; } ctx->resume_handler = ngx_http_lua_wev_handler; lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); c = r->connection; rc = ngx_http_lua_run_thread(lmcf->lua, r, ctx, 0); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread returned %d", rc); if (rc == NGX_AGAIN) { return ngx_http_lua_run_posted_threads(c, lmcf->lua, r, ctx); } if (rc == NGX_DONE) { ngx_http_finalize_request(r, NGX_DONE); return ngx_http_lua_run_posted_threads(c, lmcf->lua, r, ctx); } if (ctx->entered_content_phase) { ngx_http_finalize_request(r, rc); return NGX_DONE; } return rc; }
nginx_lua 还有其他很多强大的功能,像是一把瑞士军刀大大增强了nignx的扩展性和灵活性,应用场景
很多可以参照下京东的应用:http://jinnianshilongnian.iteye.com/category/333854
相关文章推荐
- nginx后端服务器返回给nginx502、504、404、执行超时等错误状态的解决方法
- 运维小知识之nginx---nginx配置Jboss集群负载均衡
- 运维小知识之nginx---CentOS6.5安装nginx配置nginx sticky
- 轻松搞定CentOS+Nginx+PHP+MySQL标准生产环境
- nginx-optimize.conf优化配置注释
- nginx-base.conf配置分析
- windows下配置Nginx+Mysql+Php7
- Nginx源码分析 - 初探Nginx的架构
- Nginx基础入门之nginx.conf配置项相关介绍
- nginx sendfile 参数解释
- Nginx安装
- nginx配置相关
- VC编译nginx
- VC编译nginx
- 安装linux学习六(4):Nginx配置文件详细说明
- [Nginx, Django]Securing connections with SSL
- 查找nginx安装的路径
- Ubuntu Linux配置Nginx+MySQL+PHP+phpMyAdmin详细步骤
- nginx
- 使Nginx支持ThinkPHP框架