目标:在nginx层面增加一个模块对请求进行处理(例如:给每个请求生成一个ID,生成ID之后,通过PHP语言可以获取到该ID)
简介:
1、NGINX对一个请求的处理过程
NGINX将http请求划分为11个处理阶段
阶段 | 说明 |
---|---|
NGX_HTTP_POST_READ_PHASE | 接收到完整的HTTP头部后处理的阶段 |
NGX_HTTP_SERVER_REWRITE_PHASE | 请求的URI与nginx.conf中的location表达式匹配前,修改请求的URI是一个独立的http阶段(重定向) |
NGX_HTTP_FIND_CONFIG_PHASE | 根据请求的URI寻找匹配的location表达式,该阶段只能由ngx_http_core_module模块实现,不建议其他http模块重新定义这一阶段的行为 |
NGX_HTTP_REWRITE_PHASE | 根据上一阶段找到的匹配的location表达式修改URI |
NGX_HTTP_POST_REWRITE_PHASE | 防止由于nginx.conf配置错误导致的重写URL后的死循环,防止死循环的处理方法:一个请求重写URL的次数达到10次就认为死循环,nginx会返回500错误 |
NGX_HTTP_PREACCESS_PHASE | NGX_HTTP_ACCESS_PHASE阶段决定访问权限前,HTTP模块 可以介入的阶段 |
NGX_HTTP_ACCESS_PHASE | 让HTTP模块判断是否允许这个请求访问nginx服务器 |
NGX_HTTP_POST_ACCESS_PHASE | 给上一阶段收尾,如果NGX_HTTP_ACCESS_PHASE阶段的HTTP模块的handler函数返回请求没有权限访问(NGX_HTTP_FORBIDEN或者NGX_HTTP_UNAUTHORIZED),该阶段向用户发送拒绝服务的错误码 |
NGX_HTTP_TRY_FILES_PHASE | 该阶段为了nginx中的try_files配置项而设置,当HTTP访问静态资源时,try_files配置项可以使请求顺序的访问多个静态资源,如果某一次访问失败,继续访问下一静态资源。这个功能是在该阶段中实现的。 |
NGX_HTTP_CONTENT_PHASE | 处理HTTP请求内容的阶段,该阶段是大部分HTTP模块最愿意介入的阶段 |
NGX_HTTP_LOG_PHASE | 处理请求完毕后记录请求日志的阶段。ngx_http_log_module模块在这一阶段加入一个handler方法,每个请求 处理完毕后记录access_log日志 |
自定义HTTP模块无法介入的阶段:
NGX_HTTP_FIND_CONFIG_PHASE
NGX_HTTP_POST_REWRITE_PHASE
NGX_HTTP_POST_ACCESS_PHASE
NGX_HTTP_TRY_FILES_PHASE
剩余7个模块均可以介入,每个阶段可以介入模块的个数也是没有限制的,多个模块可以介入同一阶段处理同一请求。
下面记录的模块介入的阶段:NGX_HTTP_CONTENT_PHASE(处理HTTP请求内容)
介入NGX_HTTP_CONTENT_PHASE的方法有两种,一种介入方法是处理所有请求,一种介入方法是只处理特定的请求。
下面的介入方法是处理所有请求。
NGINX中关于HTTP阶段的一些处理方法见下一篇(链接)
1、增加编译的配置文件(将自定义HTTP模块编译进nginx)
自定义模块的一般目录结构:
+ ngx_http_xxxxxx_module
-config
-ngx_http_xxxxxx_module.c
config文件:编译nginx可执行文件时,会读取该文件获取模块相关路径及模块名称等
ngx_http_xxxxxx_module.c :处理http请求的代码文件
举例:
ngx_addon_name=ngx_http_xxxxxx_module
HTTP_MODULES="$HTTP_MODULES ngx_http_xxxxxx_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_xxxxxx_module.c"
// ngx_addon_name:模块名称
// HTTP_MODULES:所有的http处理模块,自定义模块前一定要加上$HTTP_MODULES,该变量表示已经存在的http模块
// NGX_ADDON_SRCS:模块源文件,$NGX_ADDON_SRCS表示已有的模块源文件路径,$ngx_addon_dir表示使用.configure命令时输入的路径
// 编译命令:
// .configure --prefix=/usr/local/adinf/nginx-1.10.3 --with-stream --with-stream_ssl_module --with-http_ssl_module --with-threads --add-module=/data1/htdocs/ngx_http_xxxxxx_module
2、ngx_http_xxxxxx_module.c部分
简介:
nginx中的模块分类几类:核心模块,事件模块,http模块,upstream模块,邮件模块等
核心模块
typedef struct {
ngx_str_t xxxxxx_config_value;
} ngx_http_xxxxxx_conf_t;
static ngx_command_t ngx_http_xxxxxx_commands[] = {
{
ngx_string("xxxxxx"), // nginx.conf中,该模块会处理的配置项名称
NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, // 该名称的配置项在conf中允许的配置方式;NGX_HTTP_SRV_CONF表示允许在server块下出现,NGX_CONF_TAKE1表示配置项名称后可以跟一个参数
ngx_http_xxxxxx_handler_conf, //解析配置该配置项对应的方法
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_xxxxxx_conf_t, xxxxxx),//该配置项对应的参数会被保存到ngx_http_xxxxxx_conf_t的成员xxxxxx中
NULL
},
ngx_null_command
};
static ngx_http_module_t ngx_http_xxxxxx_module_ctx = {
ngx_http_xxxxxx_add_variable, /* preconfiguration */
ngx_http_xxxxxx_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
ngx_http_xxxxxx_create_srv_conf, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_xxxxxx_create_loc_conf, /* create location configuration */
ngx_http_xxxxxx_merge_loc_conf, /* merge location configuration */
};
ngx_module_t ngx_http_xxxxxx_module = {
NGX_MODULE_V1,
&ngx_http_xxxxxx_module_ctx, /* module context */
ngx_http_xxxxxx_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_http_xxxxxx_module:
nginx中有一个模块的数据结构ngx_module_t
,所有的nginx模块都要遵循ngx_module_t
的定义。nginx启动时会调用模块中定义的方法。ngx_module_t
中最重要的是要设置ctx
和commands
两个成员。ctx
用于指向一类 模块的上下文,http类模块中需要指向ngx_http_module_t
结构体。commands将处理nginx.conf中的配置项。
NGX_HTTP_MODULE表示该模块为http模块。
NGX_MODULE_V1
和NGX_MODULE_V1_PADDING
两个宏定义模块中未用到的成员的初始值。
上面的定义中ctx指向ngx_http_xxxxxx_module_ctx
,commands设置为ngx_http_xxxxxx_commands
。
ngx_http_xxxxxx_module_ctx:
ngx_http_module_t结构体定义了8个阶段,nginx启动过程中,会调用结构体定义的相关方法。
调用顺序:
阶段 | 说明 |
---|---|
create main configuration | 创建存储main级别直属于http块的结构体 |
create server configuration | 创建存储conf中直属于server块的配置的结构体 |
create location configuration | 创建存conf中直属于location块的配置的结构体 |
preconfiguration | 解析配置文件前 |
init main configuration | 初始化main级别配置项 |
merge server configuration | 合并main级别和server级别下同名的配置项 |
merge location configuration | 合并server级别和location级别下的同名配置项 |
postconfiguration | 完成配置项解析 |
示例代码中
方法ngx_http_xxxxxx_add_variable
在解析配置文件前创建一个了一个变量,具体方法实现看后续代码
方法ngx_http_xxxxxx_init
则设置了NGX_HTTP_CONTENT_PHASE阶段的处理http请求的方法,具体方法实现看后续代码
ngx_http_xxxxxx_commands:解析nginx.conf
// ngx系统方法解析配置项
static char * ngx_http_xxxxxx_handler_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_conf_set_str_slot(cf, cmd, conf); //调用ngx_conf_set_str_slot 处理ngx_str_t类型的变量
return NGX_CONF_OK;
}
ngx_http_xxxxxx_handler_conf方法解析nginx.conf配置,会将config中配置的值解析到结构体ngx_http_xxxxxx_conf_t中。该方法在nginx启动过程中被调用。
static ngx_str_t xxxxxx_variable = ngx_string("xxxxxx_variable"); // 要添加的变量名
// 增加变量
static ngx_int_t ngx_http_xxxxxx_add_variable(ngx_conf_t *cf){
ngx_http_variable_t * var;
var = ngx_http_add_variable(cf, &xxxxxx_variable, 0);
if (var == NULL) {
return NGX_OK;
}
//设置回调
var->get_handler = ngx_http_variable_xxxxxx_variable; // 获取该变量时的回调方法
var->data = 0;
return NGX_OK;
}
typedef struct {
ngx_str_t xxxxxx_value;
} ngx_http_xxxxxx_ctx_t;
ngx_int_t ngx_http_variable_xxxxxx_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_xxxxxx_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_xxxxxx_module);
if (ctx == NULL) {
v->not_found = 1;
return NGX_OK;
}
v->len = ctx->xxxxxx_value.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ctx->xxxxxx_value.data;
return NGX_OK;
}
nginx中的变量分为三种:模块内置变量
、根据配置动态添加的变量
、内置规则变量
该示例中添加的变量属于模块内置变量
。
根据配置动态添加的变量一般当解析相应指令时,在指令的解析函数中添加。
内置规则变量不需要添加,而是按特定规则解析,例如http、upstream_http、arg、cookie等变量都是nginx根据请求解析的。
模块内置变量主要是在ngx_http_module_t
的preconfiguration
阶段中添加(也即在nginx框架解析配置文件前)。
模块内置变量和根据配置动态添加的变量都是通过调用ngx_http_add_variable()
方法添加变量。ngx_http_add_variable
方法会向存储配置项的结构体ngx_conf_t->variable_key
数组中添加变量(参数是ngx_conf_t
和参数名
等)并返回变量结构。
// nginx中变量结构体的数据结构(并非示例代码)
typedef ngx_variable_value_t ngx_http_variable_value_t;
typedef struct {
unsigned len:28; // 变量值数据长度
unsigned valid:1; // 该变量值是否可用
unsigned no_cacheable:1; // 该变量值是否不能缓存
unsigned not_found:1; // 对应变量不存在
unsigned escape:1; // 变量值内容中的特殊字符是否进行了转义
u_char *data; // 变量值的数据
} ngx_variable_value_t;
nginx所有变量都存储在ngx_http_core_module
的main
级别的结构体ngx_http_core_main_conf_t
中,所以变量的作用范围是整个http{}配置。在某个server中添加的变量,在另一个server同样可以使用。
本示例每个http请求处理过程中都会更新一下变量,所以每个请求变量值都不一样
// 设置某个阶段处理请求的方法
static ngx_int_t ngx_http_xxxxxx_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *w;
ngx_http_core_main_conf_t *cmcf;
cmcf = (ngx_http_core_main_conf_t*)ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
//在NGX_HTTP_CONTENT_PHASE中介入处理代码,回调函数ngx_http_xxxxxx_handler_request可对http请求做处理
w = (ngx_http_handler_pt*)ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (w == NULL)
{
return NGX_OK;
}
//具体实现的回调函数
*w = ngx_http_xxxxxx_handler_request;
return NGX_OK;
}
nginx相关配置:
- nginx.conf中在server下增加配置项 xxxxxx xxxxxxxxxx
- fastcgi_params中增加变量配置
- php中通过$_SERVER['变量名']使用,变量名 使用fastcgi_params中配置的变量|