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_file757 ...768 clcf->handler = ngx_http_lua_content_handler;//使用按需挂载处理函数的方式进行挂载,处理函数为ngx_http_lua_content_handler769770 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_t136 ngx_http_lua_content_handler(ngx_http_request_t *r)137 {138 ...152153 ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);//获取请求在ngx_http_module模块对应的上下文结构154155 dd("ctx = %p", ctx);156157 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 }163164 dd("entered? %d", (int) ctx->entered_content_phase);165166 ...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_state296 ctx->vm_state = cln->data;297298 } 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 }301302 |
|
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_t230 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_state252253 /* 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_t205 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;286287 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_t35 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++ */5354 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_t599 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代码的加载的更多相关文章
- openresty源码剖析——lua代码的加载
##Openresty是什么 OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,通过把lua嵌入到Nginx中,使得我们可以用轻巧的lua语言进行nginx的相关开发,处理 ...
- js文件代码未加载或者没有js效果
问题:在页面中js文件中的代码未加载或者没有任何效果. 原因: 成功引用了js文件,但无效果或者提示未加载该文档中的代码. 可能页面引用js文件的路径存在问题 解决: 重新检查你引用的js文件的路径是 ...
- cocos2dx lua中异步加载网络图片,可用于显示微信头像
最近在做一个棋牌项目,脚本语言用的lua,登录需要使用微信登录,用户头像用微信账户的头像,微信接口返回的头像是一个url,那么遇到的一个问题就是如何在lua中异步加载这个头像,先在引擎源码里找了下可能 ...
- Style样式的四种使用(包括用C#代码动态加载资源文件并设置样式)
Posted on 2012-03-23 11:21 祥叔 阅读(2886) 评论(6) 编辑 收藏 在Web开发中,我们通过CSS来控制页面元素的样式,一般常用三种方式: 1. 内联样式 ...
- WPF中Style文件的引用——使用xaml代码或者C#代码动态加载
原文:WPF中Style文件的引用--使用xaml代码或者C#代码动态加载 WPF中控件拥有很多依赖属性(Dependency Property),我们可以通过编写自定义Style文件来控制控件的外观 ...
- 最简破解-java代码热加载热部署IDEA插件JRebel
如果经济实力允许的话,还是建议大家去购买收费版.支持原创作者,才能有更好的产品出现. 一.Jrebel插件介绍 JRebel一款帮助我们在开发过程中实现热加载的插件,目前来说,在IDEA中实现热加载最 ...
- Lua学习之加载其他lua文件
Lua 中提供了模块的概念,模块类似一个封装库或者 C++ 中的一个类,可以将公用的部分提到一个文件中,以 API 的形式供其他 lua 文件调用. Lua 中的模块其实就是包含变量.函数等已知元素组 ...
- Lua模块的加载与内存释放
今天早上听说一件事情让我觉得很诡异的事情:公司线上的一款游戏,加载一份配置资源后,内存涨了几十M,然后内存再也下不来了.因为好奇,所以要来了最大的一个配置文件(4.5M,去除空格与换行后的大小),进行 ...
- JS代码的加载
HTML页面中JS的加载原理:在加载HTML页面的时候,当浏览器遇到内嵌的JS代码时会停止处理页面,先执行JS代码,然后再继续解析和渲染页面.同样的情况也发生在外链的JS文件中,浏览器必须先花时间下载 ...
随机推荐
- 使用xlrd模块从excel文件中导入数据
- 【python密码学编程】6.凯撒加密法
凯撒加密法的迷药是0~25的整数 # _*_ coding:utf-8 _*_ #Caeser Ciper import pyperclip messgae = 'this is my secret ...
- Android -- 从源码的角度一步步打造自己的TextView
1,自定义控件一直是我们的痛点,今天就和大家一点点去了解了解,首先一般的自定义控件都是继承于View类,所以我们先来看看view的一些重要的方法,这是官方文档,大家想了解更多也可以去看看,这里我展示对 ...
- 敏捷视界:Scrum起源、Scrum术语
Scrum起源 Scrum的原始含义 Scrum原始含义是指英式橄榄球次要犯规时在犯规地点对阵争球.争球双方各有8个队员参与,各方出3名前锋队员,并肩各站成一横排,面对面躬身互相顶肩,中间形成一条通道 ...
- [算法题] Reverse Linked List
题目内容 题目来源:LeetCode Reverse a singly linked list. 题目思路 这个属于经典问题,链表反转的思路基本上已经非常固定了.有两种非常常见的方法:1.三指针法 2 ...
- python学习之核心数据类型
python核心数据类型 对象类型 例子 数字 1234,-345 字符串 'spam' 列表 [1,3,'ds'] 元组 (1,'spam',6) 字典 {'name':'lili','age':1 ...
- 机器学习笔记——拉格朗日乘子法和KKT条件
拉格朗日乘子法是一种寻找多元函数在一组约束下的极值方法,通过引入拉格朗日乘子,可将有m个变量和n个约束条件的最优化问题转化为具有m+n个变量的无约束优化问题.在介绍拉格朗日乘子法之前,先简要的介绍一些 ...
- python---------函数练习题
2.写函数,,用户传入修改的文件名,与要修改的内容,执行函数,完成整个文件的批量修改操作 # 方法一 # import os # def fun(): #y为要修改的内容,z为修改的结果 # y=in ...
- Markdown学习笔记(一) 基本的Markdown标签
Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式. Markdown的语法简洁明了.学习容易,而且功能比纯文本更强,因此有很多人用它写 ...
- nopCommerce安装教程
nopCommerce是一个通用的电子商务平台,适合每个商家的需要:它强大的企业和小型企业网站遍布世界各地的公司销售实体和数字商品.nopCommerce是一个透明且结构良好的解决方案,它结合了开源和 ...