• lua代码的加载


    lua代码的加载

    Openresty是什么

    OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,通过把lua嵌入到Nginx中,使得我们可以用轻巧的lua语言进行nginx的相关开发,处理高并发,扩展性极高的动态 Web 应用、Web 服务和动态网关。

    大家知道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来更新代码

    那openresty是如何实现这些,如何加载代码,并且是如何缓存代码的呢?

    ##Nginx配置

    假设Nginx相关的配置如下所示

    1
    lua_code_cache off;
    1
    2
    3
    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来处理

    比如

    /api/addition                    交给 lua/addition.lua 处理

    /api/lua/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

    配置项相关 

    1
    2
    3
    4
    5
    6
    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 }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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 }

    处理函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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 }

     

    创建上下文结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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) {//如果<span style="font-family: -webkit-standard; white-space: normal">lua_code_cache off</span>
    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,这样所有请求都会使用<span style="font-family: -webkit-standard; orphans: 2; white-space: normal; widows: 2">ngx_http_lua_module模块的lua_state</span>
    300     }
    301
    302
    1
      

    276行 如果关闭了lua代码缓存,那么openresty就会为每一个请求创建新的lua_state 并设置相关的字段,最后赋值给ctx->vm_state,ctx->vm_state的类型如下

      

    1
    2
    3
    4
    typedef struct {
         lua_State       *vm;
         ngx_int_t        count;
     } ngx_http_lua_vm_state_t;
     

     回调函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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 }    

     

    ##代码加载

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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) {//全局变量中存在,则返回
    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中加载代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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,这样请求来临的时候在全局变量中找不到对应的代码缓存,都需要到下一步ngx_http_lua_clfactory_loadfile中读取文件加载

    如果代码缓存打开的时候,openresty会使用ngx_http_lua_module全局的lua_state,这样只有新的lua文件 在首次加载时需要到下一步ngx_http_lua_clfactory_loadfile中 读取文件加载,第二次来的时候 便可以在lua_state对应的全局变量中找到了

     

     

    从文件中读取代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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 " end"

    700行用自定义的ngx_http_lua_clfactory_getF函数读取lua代码

    在原有代码的开头加上了return function()  结束处加上了 end

     

    缓存代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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

    122行 将 LUA_REGISTRYINDEX从栈中弹出

    125行因为代码块是 return function() … end包裹的  所以在56行需要再调用lua_pcall执行以下以获得返回的函数

     

    ##总结

    1、当lua_code_cache off的情况下  openresty关闭lua代码缓存,为每一个请求都创建一个独立的lua_state,这样每一个请求来临的时候 在新创建的lua_state中 都没有代码记录 需要重新读取文件加载代码,

    因此可以立即动态加载新的lua脚本,而不需要reload nginx,但因为每个请求都需要分配新的lua_state,和读取文件加载代码,所以性能较差

    2、当lua_code_cache on的情况下 openresty打开lua代码缓存,每一个请求使用ngx_http_lua_module全局的lua_state,新的lua文件在首次加载的时候,会去读取文件,然后存放到lua的全局变量中,请求再次的时候 就会在lua_state全局变量中找到了,

    因此修改完代码之后,需要reload nginx之后 才可以生效

    3、通过 content_by_lua_file 中使用 Nginx 变量时,可以在实现在lua_code_cache on的情况下动态加载新的 Lua 脚本的,而不需要reload nginx

  • 相关阅读:
    代数基本定义
    离散数学CONDITIONAL STATEMENTS
    欧几里德算法及其实现
    欧几里得 算法复杂度
    养成习惯
    解决在myeclipse中启动服务器就进入调试模式的方法
    oracle中对索引的操作
    schema在oracle里是什么意思(转:http://shanbei.info/schemainoraclewhatismeantin.html)
    java 加载properties 文件
    java.io.IOException: mark() not supported
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/6794470.html
Copyright © 2020-2023  润新知