Nginx是事件驅動的異步處理方式,Lua語言自己是同步處理,可是Lua原生支持協程,給Nginx與Lua的結合提供了機會。編程
Nginx能夠同時處理數以萬計的網絡鏈接,Lua能夠同時存在不少協程,簡單一點想,對每一個到來的網絡鏈接,建立一個新的協程去處理,處理完畢後釋放協程。和Apache爲每一個鏈接fork一個進程處理的流程十分類似,只不過多個進程換成了多個協程。網絡
協程相比較進程佔用資源很小,協程之間的切換性能消耗很是小,幾乎就至關於函數調用同樣。以同步的方式寫程序,實現了異步處理的效率。固然實際的編程實現並無多進程那麼簡單。異步
在Lua中,每一個協程對應有一個lua_State結構體, 這個結構體中保存了協程的全部信息。全部的協程共享一個global_State結構體,這個結構體保存全局相關的一些信息,主要是全部須要垃圾回收的對象。socket
一般建立Lua執行環境都是從lua_open(即luaL_newstate)開始, lua_open會建立一個global_State結構,建立一個協程做爲主協程。ngx_http_lua_module是在讀取配置後的postconfiguration階段建立Lua環境的,除此以外還作了一個額外的操做,主要是建立了名爲ngx,類型爲table的全局變量,全部Lua與Nginx的交互都是經過ngx這個全局變量來實現的,如ngx.sleep, ngx.socket等方法都在這個的table中。函數
Nginx中請求的處理是分階段的,ngx_http_lua_module在多個階段掛載了回調函數,這裏盜個圖.post
在rewrite, access 等多個階段,都有相應的*_by_lua*處理。性能
這裏以access階段爲例。先經過ngx_http_lua_get_lua_vm獲取主協程的lua_State結構體L,再經過ngx_http_lua_cache_loadbuffer獲取解析後的lua代碼,而後經過ngx_http_lua_access_by_chunk執行lua代碼。lua
ngx_int_t ngx_http_lua_access_handler_inline(ngx_http_request_t *r) { ngx_int_t rc; lua_State *L; ngx_http_lua_loc_conf_t *llcf; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, llcf->access_src.value.data, llcf->access_src.value.len, llcf->access_src_key, (const char *) llcf->access_chunkname); if (rc != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } return ngx_http_lua_access_by_chunk(L, r); }
在balancer_by_lua*, header_filter, body_filter, log階段中,直接在主協程中執行代碼,而在access,content等其餘幾個階段中,會建立一個新的協程去執行此階段的lua代碼。表如今API層面,二者的區別就是可否執行ngx.sleep, ngx.socket, ngx.thread這幾個命令。debug
Lua中的協程能夠隨時掛起,一段時間後繼續運行。在access等階段會新建協程, 新的協程只處理一個請求,能夠方便的掛起來,不會影響其餘的協程。而在log階段沒有建立新的協程,主協程是不能執行ngx.sleep等阻塞操做的。指針
Lua中的協程也是GC對象,會被系統進行垃圾回收時銷燬掉,爲了保證掛起的協程不會被GC掉,ngx_http_lua_module在全局的註冊表中建立了一個table,新建立的協程保存在table中,協程執行完畢後從table中註銷,GC時就會將已註銷的協程回收掉。
ngx_http_lua_module初始Lua運行環境時,執行ngx_http_lua_init_registry函數,在註冊表建立了幾個table,key爲ngx_http_lua_coroutines_key的table保存全部的協程。
static void ngx_http_lua_init_registry(lua_State *L, ngx_log_t *log) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "lua initializing lua registry"); /* {{{ register a table to anchor lua coroutines reliably: * {([int]ref) = [cort]} */ lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_createtable(L, 0, 32 /* nrec */); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ /* create the registry entry for the Lua request ctx data table */ lua_pushliteral(L, ngx_http_lua_ctx_tables_key); lua_createtable(L, 0, 32 /* nrec */); lua_rawset(L, LUA_REGISTRYINDEX); /* create the registry entry for the Lua socket connection pool table */ lua_pushlightuserdata(L, &ngx_http_lua_socket_pool_key); lua_createtable(L, 0, 8 /* nrec */); lua_rawset(L, LUA_REGISTRYINDEX); #if (NGX_PCRE) /* create the registry entry for the Lua precompiled regex object cache */ lua_pushlightuserdata(L, &ngx_http_lua_regex_cache_key); lua_createtable(L, 0, 16 /* nrec */); lua_rawset(L, LUA_REGISTRYINDEX); #endif /* {{{ register table to cache user code: * { [(string)cache_key] = <code closure> } */ lua_pushlightuserdata(L, &ngx_http_lua_code_cache_key); lua_createtable(L, 0, 8 /* nrec */); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ }
Nginx中處理請求都是圍繞ngx_http_request_t結構體進行了,一個ngx_http_request_t結構體表明瞭當前正在處理的一個請求。ngx_http_lua_module處理Lua腳本時要與Nginx進行交互,也要經過這個結構體實現。爲此在建立新的協程後,將相關聯的ngx_http_request_t的指針保存在了lua_State的全局變量中。
以下所示,經過ngx_http_lua_set_req將請求與協程關聯。
static ngx_inline void ngx_http_lua_set_req(lua_State *L, ngx_http_request_t *r) { lua_pushlightuserdata(L, r); lua_setglobal(L, ngx_http_lua_req_key); }
經過ngx_http_lua_get_req從lua_State中獲取協程關聯的請求。
static ngx_inline ngx_http_request_t * ngx_http_lua_get_req(lua_State *L) { ngx_http_request_t *r; lua_getglobal(L, ngx_http_lua_req_key); r = lua_touserdata(L, -1); lua_pop(L, 1); return r; }
下面這個是ngx.get_method的API的實現,很簡單的邏輯,經過ngx_http_lua_get_req獲取請求的ngx_http_request_t結構體,從結構體中把表明請求方法字符串返回。ngx_http_lua_module提供的API大都經過這種方式來實現。
static int ngx_http_lua_ngx_req_get_method(lua_State *L) { int n; ngx_http_request_t *r; n = lua_gettop(L); if (n != 0) { return luaL_error(L, "only one argument expected but got %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } ngx_http_lua_check_fake_request(L, r); lua_pushlstring(L, (char *) r->method_name.data, r->method_name.len); return 1; }