nginx处理请求流程
nginx启动时master进程加载配置文件,根据配置文件初始化监听的socket,fork worker进程。
worker进程与client建立socket连接处理请求,接收请求读取请求行、请求头、请求体。
处理请求,根据处理结果响应请求,响应行/头/体。
对应函数处理流程如下:
ngx_http_add_listening ---> ngx_http_init_connection --->
ngx_http_wait_request_handler ---> ngx_http_process_request_line --->
ngx_http_process_request_headers ---> ngx_http_process_request --->
ngx_http_handler ---> ngx_http_core_run_phases
nginx大部分event采用epoll EPOLLET边沿触发的方法来触发事件,只有listen端口的读事件是EPOLLLT水平触发。对于边沿触发,如果出现了可读事件,必须及时处理,否则可能会出现读事件不再触发,连接饿死的情况。
由此可见worker进程在处理完request_line和request_headers之后。
会进入ngx_http_core_module.c文件的phase入口函数:ngx_http_core_run_phases()
这就进入了著名的nginx处理请求十一个函数阶段。
十一个阶段,每个阶段有各自的checker和handler
通过cheker函数来判断是继续在本阶段handler,还是去下一个phase的handler处理。
或者跳到某个阶段handler处理。
typedef enum {
NGX_HTTP_POST_READ_PHASE = 0,
NGX_HTTP_SERVER_REWRITE_PHASE,
NGX_HTTP_FIND_CONFIG_PHASE,
NGX_HTTP_REWRITE_PHASE,
NGX_HTTP_POST_REWRITE_PHASE,
NGX_HTTP_PREACCESS_PHASE,
NGX_HTTP_ACCESS_PHASE,
NGX_HTTP_POST_ACCESS_PHASE,
NGX_HTTP_TRY_FILES_PHASE,
NGX_HTTP_CONTENT_PHASE,
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
ngx_http_core_run_phases执行十一阶段handler
遍历各个阶段的checker和handler 如果返回结果为NGX_AGAIN交由下一个phase处理,OK则返回。
void ngx_http_core_run_phases(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_phase_handler_t *ph;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
// ph为指向 ngx_http_phase_handler_t 结构的指针,存储了各phase的checker和handler。
ph = cmcf->phase_engine.handlers;
// while顺序执行checker
// 根据checker指引,handler处理根据处理结果进行下一步走向
while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) {
return;
}
}
}
十一阶段的handler是如何添加的
各个phase的handler 添加到ngx_http_core_module的cmcf->phase_engine.handlers数组中。
使用 ngx_http_phase_t 结构存储每个阶段可用的处理函数(handler),它实际上是动态数组 ngx_array_t,元素类型为 ngx_http_handler_pt,存储在ngx_http_core_main_conf_t中。
所以cmcf结构为ngx_http_core_main_conf_t。
参考文章:nginx phase handler处理及log phase处理_晓镁的博客-CSDN博客
ngx_http_block() 函数会调用所有模块的 postconfiguration 函数把自己的 handler 处理函数添加到 phases 数组里。
随后 nginx 执行函数 ngx_http_init_phase_handlers(),该函数会遍历 phases 数组,计算 handler 模块的数量,统计所以已经注册的 handler 数量并分配内存,再安阶段分类,把每个 handler 与对应阶段的 checker 组合起来,填入引擎数组。
for (m = 0; ngx_modules[m]; m++) {
...
if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
...
if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
编译nginx模块的handler
例如编译nginx时 enable ngx_http_geoip_module
找到对应的模块代码文件,找到ngx_module_t ngx_http_geoip_module
ngx_module_t ngx_http_geoip_module = {
NGX_MODULE_V1,
&ngx_http_geoip_module_ctx, /* module context */
ngx_http_geoip_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
};
通过module context 找到 ngx_http_geoip_module_ctx 又找到 preconfiguration 为 ngx_http_geoip_add_variables,ngx_http_geoip_add_variables就是handler函数。
static ngx_http_module_t ngx_http_geoip_module_ctx = {
ngx_http_geoip_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
ngx_http_geoip_create_conf, /* create main configuration */
ngx_http_geoip_init_conf, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
模块编译部分后面再详细说明。