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 "\nend"

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

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

缓存代码

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

lua代码的加载的更多相关文章

  1. openresty源码剖析——lua代码的加载

    ##Openresty是什么 OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,通过把lua嵌入到Nginx中,使得我们可以用轻巧的lua语言进行nginx的相关开发,处理 ...

  2. js文件代码未加载或者没有js效果

    问题:在页面中js文件中的代码未加载或者没有任何效果. 原因: 成功引用了js文件,但无效果或者提示未加载该文档中的代码. 可能页面引用js文件的路径存在问题 解决: 重新检查你引用的js文件的路径是 ...

  3. cocos2dx lua中异步加载网络图片,可用于显示微信头像

    最近在做一个棋牌项目,脚本语言用的lua,登录需要使用微信登录,用户头像用微信账户的头像,微信接口返回的头像是一个url,那么遇到的一个问题就是如何在lua中异步加载这个头像,先在引擎源码里找了下可能 ...

  4. Style样式的四种使用(包括用C#代码动态加载资源文件并设置样式)

    Posted on 2012-03-23 11:21 祥叔 阅读(2886) 评论(6) 编辑 收藏 在Web开发中,我们通过CSS来控制页面元素的样式,一般常用三种方式: 1.       内联样式 ...

  5. WPF中Style文件的引用——使用xaml代码或者C#代码动态加载

    原文:WPF中Style文件的引用--使用xaml代码或者C#代码动态加载 WPF中控件拥有很多依赖属性(Dependency Property),我们可以通过编写自定义Style文件来控制控件的外观 ...

  6. 最简破解-java代码热加载热部署IDEA插件JRebel

    如果经济实力允许的话,还是建议大家去购买收费版.支持原创作者,才能有更好的产品出现. 一.Jrebel插件介绍 JRebel一款帮助我们在开发过程中实现热加载的插件,目前来说,在IDEA中实现热加载最 ...

  7. Lua学习之加载其他lua文件

    Lua 中提供了模块的概念,模块类似一个封装库或者 C++ 中的一个类,可以将公用的部分提到一个文件中,以 API 的形式供其他 lua 文件调用. Lua 中的模块其实就是包含变量.函数等已知元素组 ...

  8. Lua模块的加载与内存释放

    今天早上听说一件事情让我觉得很诡异的事情:公司线上的一款游戏,加载一份配置资源后,内存涨了几十M,然后内存再也下不来了.因为好奇,所以要来了最大的一个配置文件(4.5M,去除空格与换行后的大小),进行 ...

  9. JS代码的加载

    HTML页面中JS的加载原理:在加载HTML页面的时候,当浏览器遇到内嵌的JS代码时会停止处理页面,先执行JS代码,然后再继续解析和渲染页面.同样的情况也发生在外链的JS文件中,浏览器必须先花时间下载 ...

随机推荐

  1. ThinkPHP5.0相关

    1.tp5的下载安装 使用git克隆下面的仓库地址,这个地址下载的速度比较快,差不多两分钟的时间. 克隆tp5的应用项目: git clone https://github.com/top-think ...

  2. WIN10-64+CUDA8.0+OpenCV3.0+VS2015配置

    在网上参考了各位前辈的攻略后,捣鼓了两天总算配置成功了. 目前网上多的还是ubuntu上配置CUDA的教程比较多比较详尽,但是我现在就是想在windows10上跑啊,就是想用CUDA啊... 1.CU ...

  3. Java 冒泡排序的实现

    实现原理: 比较相邻的元素.如果第一个比第二个大,就交换他们两个. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. 针对所有的元素重复以上的步骤,除 ...

  4. 关于flask线程安全的简单研究

    flask是python web开发比较主流的框架之一,也是我在工作中使用的主要开发框架.一直对其是如何保证线程安全的问题比较好奇,所以简单的探究了一番,由于只是简单查看了源码,并未深入细致研究,因此 ...

  5. 【NO.10】Jmeter - 一个完整的录制脚本的过程

    上1篇介绍了"使用Jmeter对一个接口地址或者一个页面地址执行N次请求",也就是你自己干了一件从"零"开始的事情. 那么这1篇介绍"如何使用Jmeter录制'访问一个接口地址或者一个页面地址'的脚本 ...

  6. Lenovo T440p 外放没有声音

    背景:Lenovo T440p,今天突然想听会歌 外放竟然没声音,fuck!!! 任务栏声音图标也没有静音标志. 驱动出问题了?检查下驱动,正常. 找了耳机试下,正常,看来驱动真的没问题. 喇叭坏了? ...

  7. 使用Python写一个贪吃蛇

    参考代码http://blog.csdn.net/leepwang/article/details/7640880 我在程序中加入了分数显示,三种特殊食物,将贪吃蛇的游戏逻辑写到了SnakeGame的 ...

  8. 会话控制cookie和session

    Cookie Cookie简介 HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分中两次请求是否由一个客户端发出.这样的设计严重阻碍的Web程序的设计.如:在我们进行网购时, ...

  9. Java连接数据库的4中方式详解

    Java连接数据库的方式有多种:根据所需要的不同数据库驱动分,分为四种: 1:1类驱动.这就是JDBC-ODBC桥的方式. 但这种方式不适合程序的重用与维护,不推荐使用.需要数据库的ODBC驱动. 2 ...

  10. javascript-new关键字

    先上一段代码: function Person(name,age,job) { this.name=name; this.age=age; this.job=job; this.sayName=fun ...