nginx模块里的函数怎么linux 模块 执行顺序序

博客访问: 3754403
博文数量: 3918
博客积分: 6409
博客等级: 准将
技术积分: 15961
注册时间:
认证徽章:
迷彩 潜伏 隐蔽 伪装
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: Web开发
原文地址: 作者:
一、模块简介 & && &
&&&&&&&&过滤(filter)模块是过滤响应头和内容的模块,可以对回复的头和内容进行处理。它的处理时间在获取回复内容之后,向用户发送响应之前。它的处理过程分为两个阶段,过滤HTTP回复的头部和主体,在这两个阶段可以分别对头部和主体进行修改。下面函数就是分别对头部和主体进行过滤的函数,所有模块的响应内容要返回给客户端,都必须调用这两个接口:
&&&&& & ngx_http_top_header_filter(r);
&&&&& & ngx_http_top_body_filter(r, in);
二、执行顺序
&&&&& & 过滤模块的调用是有顺序的,这在编译的时候就决定了,控制编译的脚本位于auto/modules中,当编译完Nginx之后,可以在objs目录下看到一个ngx_modules.c的文件,打开这个文件能看到如下数据结构:
&&&&& & ngx_module_t *ngx_modules[] = {
&&&&&&&&& & ...
&&&&&&&&& & &ngx_http_write_filter_module,
&&&&&&&&& & &ngx_http_header_filter_module,
&&&&&&&&& & &ngx_http_chunked_filter_module,
&&&&&&&&& & &ngx_http_range_header_filter_module,
&&&&&&&&& & &ngx_http_gzip_filter_module,
&&&&&&&&& & &ngx_http_postpone_filter_module,
&&&&&&&&& & &ngx_http_ssi_filter_module,
&&&&&&&&& & &ngx_http_charset_filter_module,
&&&&& & &&&&&ngx_http_userid_filter_module,
&&&&&&&&& & &ngx_http_headers_filter_module,
&&&&&&&&& & &ngx_http_copy_filter_module,
&&&&&&&&& & &ngx_http_range_body_filter_module,
&&&&&&&&& & &ngx_http_not_modified_filter_module,
&&&&&&&&& & NULL
&&&&&&&&};
&&&&& & 从write_filter到not_modified_filter,模块的执行顺序是反向的,即最早执行的是not_modified_filter,然后各个模块依次执行。所有第三方模块只能加入到copy_filter和headers_filter模块之间执行。每个filter模块的处理函数赋值给全局变量ngx_http_top_header_filter,而前一个filter模块的处理函数赋值给局部变量ngx_http_next_header_filter,响应头和响应体过滤函数的执行顺序如下图:
图1 header_filter和body_filter执行顺序图
三、模块编译
&&&&& & Nginx可以很方便地加入第三方过滤模块。在过滤模块的目录里加入config文件,内容如下:
&&&&& & ngx_addon_name=ngx_http_example_filter_module
&&&&& & HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_example_filter_module"
&&&&& & NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_example_filter_module.c"
&&&&& & 其中,ngx_http_example_filter_module为过滤模块名称,ngx_http_example_filter_module.c是该模块的源代码。
四、输出内容
&&&&&&&&基于Nginx流式输出模式,在过滤模块中,所有输出内容都是通过一条单向链表组成,每次Nginx都是读到一部分的内容就放到链表,然后输出出去。单向链表结构如下:
&&&&& & typedef struct ngx_chain_s ngx_chain_t;
&&&&& & struct ngx_chain_s {
&&&&&&&&& & ngx_buf_t *
&&&&&&&&& & ngx_chain_t *
&&&&&&&&};
&&&&& & 一般buffer结构体可以表示一块内存,内存的起始和结束地址分别用start和end表示,pos和last表示实际的内容。如果内容已经处理过了,pos的位置就可以往后移动。如果读取到新的内容,last的位置就会往后移动。所以buffer可以在多次调用过程中使用。如果last等于end,就说明这块内存已经用完了。如果pos等于last,说明内存已经处理完了。下面是一个简单地示意图,说明buffer中指针的用法:
图2 buffer内存结构
五、过滤函数
1、响应头过滤函数
&&&&& & 响应头过滤函数的主要用处是处理HTTP响应的头,可以根据实际情况对于响应头进行修改或者添加删除。响应头过滤函数先于响应体过滤函数,而且只调用一次,所以一般是做过滤模块的初始化工作,响应头过滤函数的入口如下:
&&&&& & ngx_int_t ngx_http_send_header(ngx_http_request_t *r)
&&&&&&&&& & ...
&&&&&&&&& & return ngx_http_top_header_filter(r);
&&&&& & 该函数在向客户端发送回复的时候调用,返回值一般为NGX_OK、NGX_ERROR和NGX_AGIN,分别表示处理成功、失败和未完成。
&&&&& & ngx_http_header_filter_module过滤模块把所有的HTTP头组合成一个完成的buffer,最终由ngx_http_write_filter_module过滤模块把buffer输出。
2、响应体过滤函数
&&&&& & 响应体过滤函数是过滤响应主体的函数。对于每个请求,函数ngx_http_top_body_filter可能会被执行多次,它的入口函数如下:
&&&&& & ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
&&&&&&&&& & ngx_int_
&&&&&&&&& & ngx_connection_t *c;
&&&&&&&&&&&&
&&&&&&&&& & c = r->
&&&&&&&&&&&&
&&&&&&&&& & rc = ngx_http_top_body_filter(r, in);
&&&&&&&&& & if (rc == NGX_ERROR) {
&&&&&&&&&&&&& & /* NGX_ERROR may be returned by any filter */
&&&&&&&&&&&&& & c->error = 1;
&&&&&&&&&&&&}
&&&&&&&&& &
&&&&& & 对于整个请求的处理阶段来说,ngx_http_output_filter的作用就是把响应内存过滤,然后发给客户端,具体模块的响应体过滤函数的格式会类似如下:
&&&&& & static int ngx_http_example_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
&&&&&&&&& & ...
&&&&&&&&& & return ngx_http_next_body_filter(r, in);
&&&&& & 该函数的返回值一般是NGX_OK、NGX_ERROR和NGX_AGAIN,分别表示处理成功、失败和未完成。
&&&&&&&&响应的主体内容就存于单链表in,链表一般不会太长,有时in参数可能为NULL。in中存有buf结构体中,对于静态文件,这个buf大小默认是32K;对于反向代理的应用,这个buf可能是4k或者8k。为了保持内存的低消耗,Nginx一般不会分配过大的内存,处理的原则是收到一定的数据,就发送出去。
&&&&&&&&在响应体过滤模块中,尤其要注意的是buf的标志位,具体可以参看图2。如果buf中包含last标志,说明是最后一块buf,可以直接输出并结束请求了。如果有flush标志,说明这块buf需要马上输出,不能缓存。如果整块buffer经过处理完以后,没有数据了,你可以把buffer的sync标志置上,表示只是同步的用处。当所有的过滤模块都处理完毕时,在最后的write_fitler模块中,Nginx会将in输出链拷贝到r->out输出链的末尾,然后调用sendfile或者writev接口输出。由于Nginx是非阻塞的socket接口,写操作并不一定会成功,可能会有部分数据还残存在r->out。在下次的调用中,Nginx会继续尝试发送,直至成功。
六、子请求
&&&&&&&&Nginx过滤模块一大特色就是可以发出子请求,即在过滤响应内容的时候,你可以发送新的请求,Nginx会根据你调用的先后顺序,将多个回复的内容拼接成正常的响应主体。一个简单的例子可以参考addtion模块。当Nginx发出子请求时,就会调用ngx_http_subrequest函数,将子请求插入父请求的r->postponed链表中。子请求会在主请求执行完毕时获得依次调用。子请求同样会有一个请求所有的生存期和处理过程,也会进入过滤模块流程。
&&&&&&&&关键点是在postpone_filter模块中,它会拼接主请求和子请求的响应内容。r->postponed按次序保存有父请求和子请求,它是一个链表,如果前面一个请求未完成,那后一个请求内容就不会输出。当前一个请求完成时并输出时,后一个请求才可输出,当所有的子请求都完成时,所有的响应内容也就输出完毕了。
阅读(210) | 评论(0) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。modules=&$CORE_MODULES $EVENT_MODULES&
modules=&$modules $HTTP_MODULES $HTTP_FILTER_MODULES \
$HTTP_HEADERS_FILTER_MODULE \
$HTTP_AUX_FILTER_MODULES \
$HTTP_COPY_FILTER_MODULE \
$HTTP_RANGE_BODY_FILTER_MODULE \
$HTTP_NOT_MODIFIED_FILTER_MODULE&
第三方开发的module应该放到$HTTP_AUX_FILTER_MODULES中。
modules 中定义了core modules event module http modules http filter module 还有第三方modules (即$HTTP_AUX_FILTER_MODULES) 接下来是如下三个filter modules。
HTTP_COPY_FILTER_MODULE -- ngx_http_copy_filter_module
HTTP_RANGE_BODY_FILTER_MODULE -- ngx_http_range_body_filter_module
HTTP_NOT_MODIFIED_FILTER_MODULE -- ngx_http_not_modified_filter_module
从后面的分析中可以看出,这三个filter在filter的处理链中将会先执行。
handler module
在Nginx配置初始化过程中,将按modules中的次序依次初始化handler modules。可分为两个过程:
将handler module的handler回调函数加入到phase对应的数组;
在phase_engine中设置phase对应的checker与handler.
一 般说来,每个module 定义了ngx_http_xxx_module_ctx变量,ngx_http_xxx_module_ctx中定义了在 postconfiguration回调函数。在读入配置文件后调用postconfiguration回调函数。 Http模块。在初始化配置过程中,Nginx根据objs/ngx_modules.c中 modules数组 的先后次序对各个module进行初始化操作,包括读入配置文件,以及调用postconfiguration函数等。
在postconfiguration() 过程中,将handler加入到对应的HTTP phase中。
nginx中的处理一个http的请求分为了如下几个phase:
ngx_http_phases:这几个phase的执行是严格按照顺序的,也就是 NGX_HTTP_POST_READ_PHASE 是第一个,而 LOG_PHASE是最后一个。只有一个特殊那就是 FIND_CONFIG_PHASE ,这个的话,有可能会在后面的rewrite phase再来调用这个phase。
在 ngx_http_core_main_conf_t中有一个包含了ngx_http_phase_t结构的数组(phases)。每一个phase包含 一个动态的handlers数组。 module的 postconfiguration() 实现了将该module的handler加入到某个phase中的handlers数组中。
例如,对于static模块, postconfiguration()即为如下函数。
static ngx_int_t
ngx_http_static_init(ngx_conf_t *cf)
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *
cmcf = ngx_http_conf_get_module_main_conf(cf ngx_http_core_module);
h = ngx_array_push(&cmcf-&phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL)
*h = ngx_http_static_
return NGX_OK;
如果多个modules的handler需要对应于NGX_HTTP_ACCESS_PHASE,则这些module的handler按序保存在如下array中:&cmcf-&phases[NGX_HTTP_ACCESS_PHASE].handlers
接下来在 ngx_http_init_phase_handlers() 函数中,设置cmcf-&phase_engine.handlers数组中每个元素的checker回调函数与handler回调函数(此即为module 的处理过程)。
对于某些模块,ngx_http_xxx_module_ctx中未定义postconfiguration。我想,不会加入到phase.
(2)执行次序
从worker接收到http请求,有如下调用过程:
nginx_accept-&ngx_init_connection-&... -& ngx_http_process_request -& ngx_http_core_run_phases
在ngx_http_core_run_phases中调用cmcf-&phase_engine中的checker回调函数来处理http请求。Handler module的checker回调函数在ngx_http_init_phase_handlers()函数中的指定。checker回调函数会调用handler module 的handler 回调函数。
如下所示为ngx_http_core_run_phases函数。
ngx_http_core_run_phases(ngx_http_request_t *r)
ngx_http_phase_handler_t *
ngx_http_core_main_conf_t *
cmcf = ngx_http_get_module_main_conf(r ngx_http_core_module);
ph = cmcf-&phase_engine.
while (ph[r-&phase_handler].checker)
checker回调函数将可能改变 r-&phase_handler 的值,并返回如下值:
NGX_OK -- 表示该请求已经处理结束。
NGX_AGAIN -- 表示还将执行另一个phase的处理。
filter module
同 handler的初始化比较类似,但是具体过程更简单些。module 相关的ngx_http_xxx_module_ctx变量中定义了 postconfiguration回调函数,该回调函数将完成把该filter module加入到filter链中的功能: 这里将当前filter module加入到filter链的top位置,原来位于top 的filter module 变为filter链中的next元素。因此在执行filter 链时,后加入到filter链中的filter module将先执行。
static ngx_int_t
ngx_http_chunked_filter_init(ngx_conf_t *cf)
ngx_http_next_header_filter = ngx_http_top_header_
ngx_http_top_header_filter = ngx_http_chunked_header_
ngx_http_next_body_filter = ngx_http_top_body_
ngx_http_top_body_filter = ngx_http_chunked_body_
return NGX_OK;
在配置初始化过程中,依次处理objs/ngx_modules.c中 modules数组中的filter:ngx_module_t *ngx_modules[] =;
(2)执行次序
ngx_http_send_header() -& ngx_http_top_header_filter
ngx_http_output_filter() -& ngx_http_top_body_filter
在handler module的处理函数中将可能调用ngx_http_send_header()和ngx_http_output_filter()入口进入到filter链中进行header或body的处理。
例如,如下是gzip_static的handler函数。
static ngx_int_t
ngx_http_gzip_static_handler(ngx_http_request_t *r)
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc & NGX_OK || r-&header_only)
b-&file_pos = 0;
b-&file_last = of.
b-&in_file = b-&file_last ? 1 : 0;
b-&last_buf = 1;
b-&last_in_chain = 1;
b-&file-&fd = of.
b-&file-&name =
b-&file-&log =
b-&file-&directio = of.is_
out.next = NULL;
return ngx_http_output_filter(r &out);
延伸阅读:
  Django的部署可以有很多方式,采用nginx+uwsgi的...
本教程为 李华明 编著的iOS-Cocos2d游戏开发系列教程:教程涵盖关于i......
专题主要学习DirectX的初级编程入门学习,对Directx11的入门及初学者有......
&面向对象的JavaScript&这一说法多少有些冗余,因为JavaScript 语言本......
Windows7系统专题 无论是升级操作系统、资料备份、加强资料的安全及管......Nginx入门之两种handler函数的挂载方式 - 推酷
Nginx入门之两种handler函数的挂载方式
请在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
接着上次的文章,今天研究《深入理解Nginx模块开发与架构解析》一书中给出的mytest的例子,发现和
一书中的例子不甚相同,尤其是在handler函数的挂在方面。
函数的挂载分为两种方式:
一种方式就是按处理阶段挂载;另外一种挂载方式就是按需挂载。tengine.taobao.org 中使用的挂载方式是按处理阶段挂载,而深入理解一书中的挂载方式是按需求挂载。
中的例子:
一文,handler_init就是handler函数的挂载函数,该函数在上下文结构中的postconfiguration字段被调用,决定handler函数在哪里被挂载。
1 static ngx_http_module_t ngx_http_hello_module_ctx = {
/* preconfiguration */
ngx_http_hello_init,
/* postconfiguration */
/* create main configuration */
/* init main configuration */
/* create server configuration */
/* merge server configuration */
ngx_http_hello_create_loc_conf, /* create location configuration */
1 static ngx_int_t
2 ngx_http_hello_init(ngx_conf_t *cf)
ngx_http_handler_pt
ngx_http_core_main_conf_t
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); //取得core_module的cf
h = ngx_array_push(&cmcf-&phases[NGX_HTTP_CONTENT_PHASE].handlers); // 挂载函数到对应处理阶段
if (h == NULL) {
return NGX_ERROR;
*h = ngx_http_hello_ //将函数指针指向handler函数
return NGX_OK;
使用这种方式挂载的handler也被称为
content phase handlers 。
而在《深入理解Nginx模块开发与架构解析》一书中给出的mytest的例子中,则是按需求挂载:
以这种方式挂载的handler也被称为
content handler 。
当一个请求进来以后,nginx从NGX_HTTP_POST_READ_PHASE阶段开始依次执行每个阶段中所有handler。执行到 NGX_HTTP_CONTENT_PHASE阶段的时候,如果这个location有一个对应的content handler模块,那么就去执行这个content handler模块真正的处理函数。否则继续依次执行NGX_HTTP_CONTENT_PHASE阶段中所有content phase handlers,直到某个函数处理返回NGX_OK或者NGX_ERROR。
换句话说,当某个location处理到NGX_HTTP_CONTENT_PHASE阶段时,如果有content handler模块,那么NGX_HTTP_CONTENT_PHASE挂载的所有content phase handlers都不会被执行了。
但是使用这个方法挂载上去的handler有一个特点是必须在NGX_HTTP_CONTENT_PHASE阶段才能执行到。如果你想自己的handler在更早的阶段执行,那就不要使用这种挂载方式。
那么在什么情况会使用这种方式来挂载呢?一般情况下,某个模块对某个location进行了处理以后,发现符合自己处理的逻辑,而且也没有必要再调用NGX_HTTP_CONTENT_PHASE阶段的其它handler进行处理的时候,就动态挂载上这个handler。
mytest这个例子在配置结构中,直接调用了content handler函数,名为ngx_http_mytest,该函数直接实现了真正的handler函数的挂载执行:
1 static ngx_command_t ngx_http_mytest_commands[] = {
ngx_string(&mytest&),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
ngx_http_mytest,
NGX_HTTP_LOC_CONF_OFFSET,
ngx_null_command
content handler函数 ngx_http_mytest的定义如下:
1 static char *
2 ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_http_core_loc_conf_t *
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf-&handler = ngx_http_mytest_
return NGX_CONF_OK;
定义十分简单,主要就是一个 ngx_http_conf_get_module_loc_conf函数的调用,以及一个函数指针的赋值。函数指针的赋值实现了真正的handler函数的挂载。
那么,我们来看看被调用的 ngx_http_conf_get_module_loc_con函数的定义:
#define ngx_http_conf_get_module_loc_conf(cf, module)
((ngx_http_conf_ctx_t *) cf-&ctx)-&loc_conf[module.ctx_index]
只是一个宏定义,主要作用是将之前存储起来的conf内容调出,赋值给clcf。
联系方式:。
个人理解,欢迎指正。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致当前位置:&>&&>&
Nginx与Lua的执行顺序和步骤说明
发布时间:
来源:服务器之家
一、Nginx执行步骤
Nginx处理每一个用户请求时,都是按照若干个不同阶段(phase)依次处理的,而不是根据配置文件上的顺序。
Nginx处理请求的过程一共划分为11个阶段,按照执行顺序依次是post-read、server-rewrite、find-config、rewrite、post-rewrite、 preaccess、access、post-access、try-files、content、log.
1、post-read
读取请求内容阶段,nginx读取并解析完请求头之后就立即开始运行;例如模块&ngx_realip&就在&post-read
阶段注册了处理程序,它的功能是迫使&Nginx&认为当前请求的来源地址是指定的某一个请求头的值。
2、server-rewrite
server请求地址重写阶段;当ngx_rewrite模块的set配置指令直接书写在server配置块中时,基本上都是运行在server-rewrite
3、find-config
配置查找阶段,这个阶段并不支持Nginx模块注册处理程序,而是由Nginx核心来完成当前请求与location配置块之间的配对工作。
4、rewrite
location请求地址重写阶段,当ngx_rewrite指令用于location中,就是再这个阶段运行的;另外ngx_set_misc(设置md5、encode_base64等)模块的指令,还有ngx_lua模块的set_by_lua指令和rewrite_by_lua指令也在此阶段。
5、post-rewrite
请求地址重写提交阶段,当nginx完成rewrite阶段所要求的内部跳转动作,如果rewrite阶段有这个要求的话;
6、preaccess
访问权限检查准备阶段,ngx_limit_req和ngx_limit_zone在这个阶段运行,ngx_limit_req可以控制请求的访问频率,ngx_limit_zone可以控制访问的并发度;
访问权限检查阶段,标准模块ngx_access、第三方模块ngx_auth_request以及第三方模块ngx_lua的access_by_lua指令就运行在这个阶段。配置指令多是执行访问控制相关的任务,如检查用户的访问权限,检查用户的来源IP是否合法;
8、post-access
访问权限检查提交阶段;主要用于配合access阶段实现标准ngx_http_core模块提供的配置指令satisfy的功能。satisfy
all(与关系),satisfy&any(或关系)
9、try-files
配置项try_files处理阶段;专门用于实现标准配置指令try_files的功能,如果前&N-1
个参数所对应的文件系统对象都不存在,try-files&阶段就会立即发起&内部跳转&到最后一个参数(即第&N
个参数)所指定的URI.
10、content
内容产生阶段,是所有请求处理阶段中最为重要的阶段,因为这个阶段的指令通常是用来生成HTTP响应内容并输出&HTTP
响应的使命;
日志模块处理阶段;记录日志
二、Nginx下Lua处理阶段与使用范围:
init_by_lua&&&&&&&&&&&&http
set_by_lua&&&&&&&&&&&&&server,&server&if,&location,&location&if
rewrite_by_lua&&&&&&&&&http,&server,&location,&location&if
access_by_lua&&&&&&&&&&http,&server,&location,&location&if
content_by_lua&&&&&&&&&location,&location&if
header_filter_by_lua&&&http,&server,&location,&location&if
body_filter_by_lua&&&&&http,&server,&location,&location&if
log_by_lua&&&&&&&&&&&&&http,&server,&location,&location&if
init_by_lua:
在nginx重新加载配置文件时,运行里面lua脚本,常用于全局变量的申请。
例如lua_shared_dict共享内存的申请,只有当nginx重起后,共享内存数据才清空,这常用于统计。
set_by_lua:
设置一个变量,常用与计算一个逻辑,然后返回结果
该阶段不能运行Output&API、Control&API、Subrequest&API、Cosocket&API
rewrite_by_lua:
在access阶段前运行,主要用于rewrite
access_by_lua:
主要用于访问控制,能收集到大部分变量,类似status需要在log阶段才有。
这条指令运行于nginx&access阶段的末尾,因此总是在&allow&和&deny&这样的指令之后运行,虽然它们同属&access
content_by_lua:
阶段是所有请求处理阶段中最为重要的一个,运行在这个阶段的配置指令一般都肩负着生成内容(content)并输出HTTP响应。
header_filter_by_lua:
一般只用于设置Cookie和Headers等
该阶段不能运行Output&API、Control&API、Subrequest&API、Cosocket&API
body_filter_by_lua:
一般会在一次请求中被调用多次,&因为这是实现基于&HTTP&1.1&chunked&编码的所谓&流式输出&的。
该阶段不能运行Output&API、Control&API、Subrequest&API、Cosocket&API
log_by_lua:
该阶段总是运行在请求结束的时候,用于请求的后续操作,如在共享内存中进行统计数据,如果要高精确的数据统计,应该使用body_filter_by_lua。
该阶段不能运行Output&API、Control&API、Subrequest&API、Cosocket&API
三、ngx_lua运行指令
ngx_lua属于nginx的一部分,它的执行指令都包含在nginx的11个步骤之中了,不过ngx_lua并不是所有阶段都会运行的;
1、init_by_lua、init_by_lua_file
语法:init_by_lua
语境:http
阶段:loading-config
当nginx master进程在加载nginx配置文件时运行指定的lua脚本,通常用来注册lua的全局变量或在服务器启动时预加载lua模块:
init_by_lua &cjson = require &cjson&&;
&&&&location&=&/api&{
&&&&&&&&content_by_lua&'
&&&&&&&&&&&&ngx.say(cjson.encode({dog&=&5,&cat&=&6}))
或者初始化lua_shared_dict共享数据:
lua_shared_dict&dogs&1m;
init_by_lua&'
&&&&local&dogs&=&ngx.shared.
&&&&dogs:set(&Tom&,&50)
&&&&location&=&/api&{
&&&&&&&&content_by_lua&'
&&&&&&&&&&&&local&dogs&=&ngx.shared.
&&&&&&&&&&&&ngx.say(dogs:get(&Tom&))
但是,lua_shared_dict的内容不会在nginx reload时被清除。所以如果你不想在你的init_by_lua中重新初始化共享数据,那么你需要在你的共享内存中设置一个标志位并在init_by_lua中进行检查。
因为这个阶段的lua代码是在nginx forks出任何worker进程之前运行,数据和代码的加载将享受由操作系统提供的copy-on-write的特性,从而节约了大量的内存。
不要在这个阶段初始化你的私有lua全局变量,因为使用lua全局变量会照成性能损失,并且可能导致全局命名空间被污染。
这个阶段只支持一些小的LUA Nginx API设置:ngx.log和print、ngx.shared.DICT;
2、init_worker_by_lua、init_worker_by_lua_file
语法:init_worker_by_lua
语境:http
阶段:starting-worker
在每个nginx worker进程启动时调用指定的lua代码。如果master 进程不允许,则只会在init_by_lua之后调用。
这个hook通常用来创建每个工作进程的计时器(通过lua的ngx.timer API),进行后端健康检查或者其它日常工作:
init_worker_by_lua:
&&&&local&delay&=&3&&--&in&seconds
&&&&local&new_timer&=&ngx.timer.at
&&&&local&log&=&ngx.log
&&&&local&ERR&=&ngx.ERR
&&&&local&check
&&&&check&=&function(premature)
&&&&&&&&if&not&premature&then
&&&&&&&&&&&&--&do&the&health&check&other&routine&work
&&&&&&&&&&&&local&ok,&err&=&new_timer(delay,&check)
&&&&&&&&&&&&if&not&ok&then
&&&&&&&&&&&&&&&&log(ERR,&&failed&to&create&timer:&&,&err)
&&&&&&&&&&&&&&&&return
&&&&&&&&&&&&end
&&&&&&&&end
&&&&local&ok,&err&=&new_timer(delay,&check)
&&&&if&not&ok&then
&&&&&&&&log(ERR,&&failed&to&create&timer:&&,&err)
3、set_by_lua、set_by_lua_file
语法:set_by_lua arg1 $arg2 &]
语境:server、server if、location、location if
阶段:rewrite
传入参数到指定的lua脚本代码中执行,并得到返回值到res中。中的代码可以使从ngx.arg表中取得输入参数(顺序索引从1开始)。
这个指令是为了执行短期、快速运行的代码因为运行过程中nginx的事件处理循环是处于阻塞状态的。耗费时间的代码应该被避免。
禁止在这个阶段使用下面的API:1、output api(ngx.say和ngx.send_headers);2、control api(ngx.exit);3、subrequest api(ngx.location.capture和ngx.location.capture_multi);4、cosocket api(ngx.socket.tcp和ngx.req.socket);5、sleep api(ngx.sleep)
此外注意,这个指令只能一次写出一个nginx变量,但是使用ngx.var接口可以解决这个问题:
location&/foo&{
&&&&set&$diff&'';
&&&&set_by_lua&$num&'
&&&&&&&&local&a&=&32
&&&&&&&&local&b&=&56
&&&&&&&&ngx.var.diff&=&a&-&b;&--写入$diff中
&&&&&&&&return&a&+&b;&&--返回到$sum中
&&&&echo&&sum&=&$sum,&diff&=&$diff&;
这个指令可以自由的使用HttpRewriteModule、HttpSetMiscModule和HttpArrayVarModule所有的方法。所有的这些指令都将按他们出现在配置文件中的顺序进行执行。
4、rewrite_by_lua、rewrite_by_lua_file
语法:rewrite_by_lua
语境:http、server、location、location if
阶段:rewrite tail
作为rewrite阶段的处理,为每个请求执行指定的lua代码。注意这个处理是在标准HtpRewriteModule之后进行的:
location&/foo&{
&&&&set&$a&12;
&&&&set&$b&&&;
&&&&rewrite_by_lua&'ngx.var.b&=&tonumber(ngx.var.a)&+&1';
&&&&echo&&res&=&$b&;
如果这样的话将不会按预期进行工作:
location&/foo&{
&&&&set&$a&12;
&&&&set&$b&'';
&&&&rewrite_by_lua&'ngx.var.b&=&tonumber(ngx.var.a)&+&1';
&&&&if($b&=&'13')&{
&&&&&&&&rewrite&^&/bar&
&&&&echo&&res&=&$b&
因为if会在rewrite_by_lua之前运行,所以判断将不成立。正确的写法应该是这样:
location&/foo&{
&&&&set&$a&12;
&&&&set&$b&'';
&&&&rewrite_by_lua&'
&&&&&&&&ngx.var.b&=&tonumber(ngx.var.a)&+&1
&&&&&&&&if&tonumber(ngx.var.b)&==&13&then
&&&&&&&&&&&&return&ngx.redirect(&/bar&);
&&&&&&&&end
&&&&echo&&res&=&$b&;
注意ngx_eval模块可以近似于使用rewite_by_lua,例如:
location&/&{
&&&&eval&$res&{
&&&&&&&&proxy_pass&http://foo,com/check-
&&&&if($res&=&'spam')&{
&&&&&&&&rewrite&^&/terms-of-use.html&
&&&&fastcgi_pass&.......
可以被ngx_lua这样实现:
location&=&/check-spam&{
&&&&proxy_pass&/check-
location&/&{
&&&&rewrite_by_lua&'
&&&&&&&&local&res&=&ngx.location.capture(&/check-spam&)
&&&&&&&&if&res.body&==&&spam&&then
&&&&&&&&&&&&return&ngx.redirect(&terms-of-use.html&)
&&&&fastcgi_pass&.......
和其它的rewrite阶段的处理程序一样,rewrite_by_lua在subrequests中一样可以运行。
请注意在rewrite_by_lua内调用ngx.exit(ngx.OK),nginx的请求处理流程将继续进行content阶段的处理。从rewrite_by_lua终止当前的请求,要调用ngx.exit返回status大于200并小于300的成功状态或ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)的失败状态。
如果HttpRewriteModule的重写指令被用来改写URI和重定向,那么任何rewrite_by_lua和rewrite_by_lua_file的代码将不会执行,例如:
location&/foo&{
&&&&rewrite&^&/
&&&&rewrite_by_lua&'ngx.exit(503)'
location&/bar&{
&&&&.......
在这个例子中ngx.exit(503)将永远不会被执行,因为rewrite修改了location,请求已经跳入其它location中了。
5、access_by_lua,access_by_lua_file
语法:access_by_lua
语境:http,server,location,location if
阶段:access tail
为每个请求在访问阶段的调用lua脚本进行处理。主要用于访问控制,能收集到大部分的变量。
注意access_by_lua和rewrite_by_lua类似是在标准HttpAccessModule之后才会运行,看一个例子:
location&/&{
&&&&deny&192.168.1.1;
&&&&allow&192.168.1.0/24;
&&&&allow&10.1.1.0/16;
&&&&access_by_lua&'
&&&&&&&&local&res&=&ngx.location.capture(&/mysql&,&{...})
&&&&&&&&....
如果client ip在黑名单之内,那么这次连接会在进入access_by_lua调用的mysql之前被丢弃掉。
ngx_auth_request模块和access_by_lua的用法类似:
location&/&{
&&&&auth_request&/
可以用ngx_lua这么实现:
location&/&{
&&&&access_by_lua&'
&&&&&&&&local&res&=&ngx.location.capture(&/auth&)
&&&&&&&&if&res.status&==&ngx.HTTP_OK&then
&&&&&&&&&&&&return
&&&&&&&&end
&&&&&&&&if&res.status&==&ngx.HTTP_FORBIDDEN&then
&&&&&&&&&&&&ngx.exit(res.status)
&&&&&&&&end
&&&&&&&&ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
和其它access阶段的模块一样,access_by_lua不会在subrequest中运行。
请注意在access_by_lua内调用ngx.exit(ngx.OK),nginx的请求处理流程将继续进行后面阶段的处理。从rewrite_by_lua终止当前的请求,要调用ngx.exit返回status大于200并小于300的成功状态或ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)的失败状态。
6、content_by_lua,content_by_lua_file
语法:content_by_lua
语境:location,location if
阶段:content
作为&content handler&为每个请求执行lua代码,为请求者输出响应内容。
不要将它和其它的内容处理指令在同一个location内使用如proxy_pass。
7、header_filter_by_lua,header_filter_by_lua_file
语法:header_filter_by_lua
语境:http,server,location,location if
阶段:output-header-filter
一般用来设置cookie和headers,在该阶段不能使用如下几个API:
1、output API(ngx.say和ngx.send_headers)
2、control API(ngx.exit和ngx.exec)
3、subrequest API(ngx.location.capture和ngx.location.capture_multi)
4、cosocket API(ngx.socket.tcp和ngx.req.socket)
有一个例子是 在你的lua header filter里添加一个响应头标头:
location&/&{
&&&&proxy_pass&http://
&&&&header_filter_by_lua&'ngx.header.Foo&=&&blah&';
8、body_filter_by_lua,body_filter_by_lua_file
语法:body_filter_by_lua
语境:http,server,location,location if
阶段:output-body-filter
输入的数据时通过ngx.arg1,通过ngx.arg[2]这个bool类型表示响应数据流的结尾。
基于这个原因,&eof&只是nginx的链接缓冲区的last_buf(对主requests)或last_in_chain(对subrequests)的标记。
运行以下命令可以立即终止运行接下来的lua代码:
return ngx.ERROR
这会将响应体截断导致无效的响应。lua代码可以通过修改ngx.arg[1]的内容将数据传输到下游的nginx output body filter阶段的其它模块中去。例如,将response body中的小写字母进行反转,我们可以这么写:
location&/&{
&&&&proxy_pass&http://
&&&&body_filter_by_lua&'ngx.arg[1]&=&string.upper(ngx.arg[1])'
当将ngx.arg[1]设置为nil或者一个空的lua string时,下游的模块将不会收到数据了。
同样可以通过修改ngx.arg[2]来设置新的&eof&标记,例如:
location&/t&{
&&&&echo&hello&
&&&&echo&hiya&
&&&&body_filter_by_lua&'
&&&&&&&&local&chunk&=&ngx.arg[1]
&&&&&&&&if&string.match(chunk,&&hello&)&then
&&&&&&&&&&&&ngx.arg[2]&=&true&--new&eof
&&&&&&&&&&&&return
&&&&&&&&end
&&&&&&&&--just&throw&away&any&remaining&chunk&data
&&&&&&&&ngx.arg[1]&=&nil
那么GET /t的请求只会回复:hello world
这是因为,当body filter看到了一块包含&hello&的字符块后立即将&eof&标记设置为了true,从而导致响应被截断了但仍然是有效的回复。
当lua代码中改变了响应体的长度时,应该要清除content-length响应头部的值,例如:
location&/foo&{
&&&&header_filter_by_lua&'ngx.header.content_length&=&nil'
&&&&body_filter_by_lua&'ngx.arg[1]&=&string.len(ngx.arg[1])&..&&\\n&'
在该阶段不能使用如下几个API:
1、output API(ngx.say和ngx.send_headers)
2、control API(ngx.exit和ngx.exec)
3、subrequest API(ngx.location.capture和ngx.location.capture_multi)
4、cosocket API(ngx.socket.tcp和ngx.req.socket)
nginx output filters可能会在一次请求中被多次调用,因为响应体可能是以chunks方式传输的。因此这个指令一般会在一次请求中被调用多次。
9、log_by_lua,log_by_lua_file
语法:log_by_lua
语境:http,server,location,location if
在log阶段调用指定的lua脚本,并不会替换access log,而是在那之后进行调用。
在该阶段不能使用如下几个API:
1、output API(ngx.say和ngx.send_headers)
2、control API(ngx.exit和ngx.exec)
3、subrequest API(ngx.location.capture和ngx.location.capture_multi)
4、cosocket API(ngx.socket.tcp和ngx.req.socket)
一个收集upstream_response_time的平均数据的例子:
lua_shared_dict&log_dict&5M
&&&&location&/&{
&&&&&&&&proxy_pass&//mybackend
&&&&&&&&log_by_lua&'
&&&&&&&&&&&&local&log_dict&=&ngx.shared.log_dict
&&&&&&&&&&&&local&upstream_time&=&tonumber(ngx.var.upstream_response_time)
&&&&&&&&&&&&local&sum&=&log_dict:get(&upstream_time-sum&)&or&0
&&&&&&&&&&&&sum&=&sum&+&upstream_time
&&&&&&&&&&&&log_dict:set(&upsteam_time-sum&,&sum)
&&&&&&&&&&&&local&newval,&err&=&log_dict:incr(&upstream_time-nb&,&1)
&&&&&&&&&&&&if&not&newval&and&err&==&&not&found&&then
&&&&&&&&&&&&&&&&log_dict:add(&upstream_time-nb&,&0)
&&&&&&&&&&&&&&&&log_dict:incr(&upstream_time-nb&,&1)
&&&&&&&&&&&&end
&&&&location&=&/status&{
&&&&&&&&content_by_lua&'
&&&&&&&&&&&&local&log_dict&=&ngx.shared.log_dict
&&&&&&&&&&&&local&sum&=&log_dict:get(&upstream_time-sum&)
&&&&&&&&&&&&local&nb&=&log_dict:get(&upstream_time-nb&)
&&&&&&&&&&&&if&nb&and&sum&then
&&&&&&&&&&&&&&&&ngx.say(&average&upstream&response&time:&&&,&sum/nb,&&&(&,&nb,&&&reqs)&)
&&&&&&&&&&&&else
&&&&&&&&&&&&&&&&ngx.say(&no&data&yet&)
&&&&&&&&&&&&end
Copyright © . 版权所有

我要回帖

更多关于 linux 模块 执行顺序 的文章

 

随机推荐