公司使用 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相关协程并继续运行。
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