##Openresty是什麼nginx
OpenResty是一個基於 Nginx 與 Lua 的高性能 Web 平臺,經過把lua嵌入到Nginx中,使得咱們能夠用輕巧的lua語言進行nginx的相關開發,處理高併發,擴展性極高的動態 Web 應用。api
你們知道lua_code_cache 開關用於控制是否緩存*_by_lua_file對應的文件裏的lua代碼緩存
lua_code_cache off的狀況下,跟請求有關的階段,在每次有請求來的時候,都會從新加載最新的lua文件,這樣咱們修改完代碼以後就不用經過reload來更新代碼了併發
而*_by_lua_block、*_by_lua和init_by_lua_file裏的代碼(init_by_lua階段和具體請求無關),若是修改的內容涉及這幾個,仍須要經過reload來更新代碼socket
那openresty是如何實現這些,如何完成加載代碼,和代碼緩存的呢?函數
##Nginx配置高併發
假設Nginx相關的配置以下所示post
lua_code_cache off;
location ~ ^/api/([-_a-zA-Z0-9/]+) { content_by_lua_file lua/$1.lua; }
當來到的請求符合 ^/api/([-_a-zA-Z0-9/] 時,會在NGX_HTTP_CONTENT_PHASE HTTP請求內容階段交給 lua/$1.lua來處理性能
好比lua
/api/addition 交給 lua/addition.lua 處理
/api/substraction 交給 lua/substraction .lua 處理
##請求的處理
content_by_lua_file對應的請求來臨時,執行流程爲 ngx_http_lua_content_handler -> ngx_http_lua_content_handler_file-> ngx_http_lua_content_by_chunk
配置項相關
324 { ngx_string("content_by_lua_file"), 325 NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, 326 ngx_http_lua_content_by_lua, 327 NGX_HTTP_LOC_CONF_OFFSET, 328 0, 329 (void *) ngx_http_lua_content_handler_file }
670 char * 671 ngx_http_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 672 { 673 ... 756 llcf->content_handler = (ngx_http_handler_pt) cmd->post;//設置回調函數爲ngx_http_lua_content_handler_file 757 ... 768 clcf->handler = ngx_http_lua_content_handler;//使用按需掛載處理函數的方式掛載處理函數,處理函數爲ngx_http_lua_content_handler 769 770 return NGX_CONF_OK; 771 }
處理函數
135 ngx_int_t 136 ngx_http_lua_content_handler(ngx_http_request_t *r)137 { 138 ... 152 153 ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);//獲取請求在ngx_http_module模塊對應的上下文結構 154 155 dd("ctx = %p", ctx); 156 157 if (ctx == NULL) { 158 ctx = ngx_http_lua_create_ctx(r);//若是以前沒有設置過上下文,調用ngx_http_lua_create_ctx建立上下文結構 159 if (ctx == NULL) { 160 return NGX_HTTP_INTERNAL_SERVER_ERROR; 161 } 162 } 163 164 dd("entered? %d", (int) ctx->entered_content_phase); 165 166 ... 205 return llcf->content_handler(r);//調用ngx_http_content_handler_file函數 206 }
建立上下文結構
259 ngx_http_lua_create_ctx(ngx_http_request_t *r) 260 { 261 ... 276 if (!llcf->enable_code_cache && r->connection->fd != (ngx_socket_t) -1) {//若是lua_code_cache off 277 ... 281 L = ngx_http_lua_init_vm(lmcf->lua, lmcf->cycle, r->pool, lmcf, 282 r->connection->log, &cln);//爲請求初始化一個新的lua_state 296 ctx->vm_state = cln->data; 297 298 } else { 299 ctx->vm_state = NULL;//不分配新的lua_state,這樣全部請求都會使用ngx_http_lua_module模塊的lua_state 300 } 301 302
276行 若是關閉了lua代碼緩存,那麼openresty就會爲每個請求建立一個新的lua_state 並設置相關的字段,最後賦值給ctx->vm_state,
ctx->vm_state的類型以下
typedef struct { lua_State *vm; ngx_int_t count; } ngx_http_lua_vm_state_t;
回調函數
229 ngx_int_t 230 ngx_http_lua_content_handler_file(ngx_http_request_t *r) 231 { 232 ... 244 script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, 245 eval_src.len);//獲取lua文件的路徑 251 L = ngx_http_lua_get_lua_vm(r, NULL);//得到lua_state,若是請求有本身的lua_state則使用請求本身的lua_state,不然使用ngx_http_lua_module模塊的lua_state 252 253 /* load Lua script file (w/ cache) sp = 1 */ 254 rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path, 255 llcf->content_src_key);//加載代碼 256 ... 267 return ngx_http_lua_content_by_chunk(L, r);//建立協程執行代碼的函數 268 }
##代碼加載
204 ngx_int_t 205 ngx_http_lua_cache_loadfile(ngx_log_t *log, lua_State *L, 206 const u_char *script, const u_char *cache_key) 207 { 208 ... 232 rc = ngx_http_lua_cache_load_code(log, L, (char *) cache_key); 233 if (rc == NGX_OK) {//代碼在全局變量table中存在,則返回 234 ... 237 return NGX_OK; 238 } 239 ... 250 rc = ngx_http_lua_clfactory_loadfile(L, (char *) script);// 251 ... 279 rc = ngx_http_lua_cache_store_code(L, (char *) cache_key); 280 ... 285 return NGX_OK; 286 287 error: 288 ... 294 }
代碼加載分紅3步完成
ngx_http_lua_cache_load_code 從lua_state的全局變量table中加載代碼,若是全局緩存中有就返回
ngx_http_lua_clfactory_loadfile 用自定義的函數從文件中加載代碼
ngx_http_lua_cache_store_code 把代碼存放到lua_state的全局變量table中
嘗試從全局變量table中加載代碼
34 static ngx_int_t 35 ngx_http_lua_cache_load_code(ngx_log_t *log, lua_State *L, 36 const char *key) 37 { 38 ... 41 /* get code cache table */ 42 lua_pushlightuserdata(L, &ngx_http_lua_code_cache_key); 43 lua_rawget(L, LUA_REGISTRYINDEX); /* sp++ */ 44 ... 52 lua_getfield(L, -1, key); /* sp++ */ 53 54 if (lua_isfunction(L, -1)) { 55 /* call closure factory to gen new closure */ 56 rc = lua_pcall(L, 0, 1, 0); 57 if (rc == 0) { 58 ... 61 return NGX_OK; 62 } 63 ... 75 return NGX_ERROR; 76 } 77 ... 85 return NGX_DECLINED; 86 }
42-52行,至關於LUA_REGISTRYINDEX[‘ngx_http_lua_code_cache_key’][‘key’]以ngx_http_lua_code_cache_key爲索引從全局註冊表表中查找key對於的value
54-61行,若是value存在而且爲一個函數,由於這裏的函數體是 return function() … end包裹的 因此在56行須要再調用lua_pcall執行下,以得到返回的函數並將返回的函數結果放到棧頂,最終將 LUA_REGISTRYINDEX從棧中移除
若是代碼緩存關閉的時候,openresty會爲每個請求建立一個新的lua_state,這樣請求來臨的時候在全局變量table中找不到對應的代碼緩存,須要到下一步ngx_http_lua_clfactory_loadfile中讀取文件加載代碼
若是代碼緩存打開的時候,openresty會使用ngx_http_lua_module全局的lua_state,這樣只有新的lua文件,在首次加載時須要到ngx_http_lua_clfactory_loadfile中讀取文件加載代碼,第二次來的時候即可以在lua_state對應的全局變量table中找到了
從文件中讀取代碼
598 ngx_int_t 599 ngx_http_lua_clfactory_loadfile(lua_State *L, const char *filename) 600 { 601 ... 615 lf.begin_code.ptr = CLFACTORY_BEGIN_CODE; 616 lf.begin_code_len = CLFACTORY_BEGIN_SIZE; 617 lf.end_code.ptr = CLFACTORY_END_CODE; 618 lf.end_code_len = CLFACTORY_END_SIZE; 619 ... 622 lf.f = fopen(filename, "r"); 623 ... 700 status = lua_load(L, ngx_http_lua_clfactory_getF, &lf, 701 lua_tostring(L, -1)); 702 ... 716 return status;
#define CLFACTORY_BEGIN_CODE "return function() "
#define CLFACTORY_END_CODE "\nend"
700行用自定義的ngx_http_lua_clfactory_getF函數讀取lua代碼
並在原有代碼的開頭加上了return function() 結束處加上了\nend
緩存代碼
103 ngx_http_lua_cache_store_code(lua_State *L, const char *key) 104 { 105 ... 108 lua_pushlightuserdata(L, &ngx_http_lua_code_cache_key); 109 lua_rawget(L, LUA_REGISTRYINDEX); 110 ... 118 lua_pushvalue(L, -2); /* closure cache closure */ 119 lua_setfield(L, -2, key); /* closure cache */ 120 ... 122 lua_pop(L, 1); /* closure */ 123 ... 125 rc = lua_pcall(L, 0, 1, 0); 126 ... 131 return NGX_OK; 132 }
108-119行,至關於 LUA_REGISTRYINDEX[‘ngx_http_lua_code_cache_key’][‘key’] = function xxx,將代碼放入全局table中
122行,將 LUA_REGISTRYINDEX從棧中彈出
125行,由於代碼塊是 return function() … end包裹的,因此在56行須要再調用lua_pcall執行以得到返回的函數
##總結
一、當lua_code_cache off的狀況下,openresty關閉lua代碼緩存,爲每個請求都建立一個新的lua_state,這樣每個請求來臨的時候在新建立的lua_state中,都在全局table的代碼緩存中找不到代碼,須要從新讀取文件加載代碼,
所以能夠當即動態加載新的lua腳本,而不須要reload nginx,但由於每一個請求都須要分配新的lua_state,和讀取文件加載代碼,因此性能較差
二、當lua_code_cache on的狀況下,openresty打開lua代碼緩存,每個請求使用ngx_http_lua_module全局的lua_state,新的lua文件在首次加載的時候,會去讀取文件加載代碼,而後存放到lua的全局變量中,
請求再次的時候 就會在lua_state全局table緩存中找到了,不須要再讀取文件加載代碼,所以修改完代碼以後,須要reload nginx以後才能夠生效
三、經過 content_by_lua_file 中使用 Nginx 變量時,能夠在實如今lua_code_cache on的狀況下動態加載新的 Lua 腳本,而不須要reload nginx