nginx phase十一个阶段
nginx的phase共有11个是众所周知的十一个处理阶段。
使用 ngx_http_phase_t 结构存储每个阶段可用的处理函数(handler)。
它实际上是动态数组 ngx_array_t,元素类型为 ngx_http_handler_pt,存储在ngx_http_core_main_conf_t中。格式如下:
typedef struct {
ngx_array_t servers; /* ngx_http_core_srv_conf_t */
#后续关注
ngx_http_phase_engine_t phase_engine;
ngx_hash_t headers_in_hash;
ngx_hash_t variables_hash;
ngx_array_t variables; /* ngx_http_variable_t */
ngx_array_t prefix_variables; /* ngx_http_variable_t */
ngx_uint_t ncaptures;
ngx_uint_t server_names_hash_max_size;
ngx_uint_t server_names_hash_bucket_size;
ngx_uint_t variables_hash_max_size;
ngx_uint_t variables_hash_bucket_size;
ngx_hash_keys_arrays_t *variables_keys;
ngx_array_t *ports;
ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;
typedef struct {
ngx_array_t handlers;
} ngx_http_phase_t;
这个其实相当一个二维链表:
引擎处理
使用二维数组 phases 可以调用所有 handler 模块,但它的组织形式不够灵活,效率也不高。
实际上nginx 不会直接使用 handler,而是为每个阶段实现一个特定的 checker 函数,在 checker 里调用 handler,并根据返回值实现阶段的灵活跳转。
存储handler/checker里用next实现阶段的快速跳转。
struct ngx_http_phase_handler_s {
// 阶段的checker函数
ngx_http_phase_handler_pt checker;
// 每个模块自己的处理函数
ngx_http_handler_pt handler;
// 指向下一个阶段第一个模块在数组里的位置
ngx_uint_t next;
};
// 所有的http请求都要使用这个引擎处理
typedef struct {
// 存储所有handler/checker的数组,里面用next实现阶段的快速跳转
ngx_http_phase_handler_t *handlers;
// server重写的跳转位置
ngx_uint_t server_rewrite_index;
// location重写的跳转位置
ngx_uint_t location_rewrite_index;
} ngx_http_phase_engine_t;
引擎的初始化:
在解析到 http{} 块时,ngx_http_block() 函数会调用所有模块的 postconfiguration 函数把自己的 handler 处理函数添加到 phases 数组里。
随后 nginx 执行函数 ngx_http_init_phase_handlers(),该函数会遍历 phases 数组,计算 handler 模块的数量,统计所以已经注册的 handler 数量并分配内存,再安阶段分类,把每个 handler 与对应阶段的 checker 组合起来,填入引擎数组。
例如:假设 POST_READ 阶段有3个模块分别注册了回调函数,SERVER_REWRITE 有2个模块注册了回调函数,那么 ph[0-2] 在 POST_READ 阶段,ph[3-4] 是在 SERVER_REWRITE 阶段。
前一个阶段的所有的 ngx_http_phase_handler_s 中 next 都指向下一个阶段的开始。比如下面3个的值都为 3,序号 3 刚好是下一个阶段 SERVER_REWRITE 序号起始。
ph[0]->next = 3
ph[1]->next = 3
ph[2]->next = 3
再例如运行时:
运行中时ph数组的内容,数组下标checker handler next。
引擎运行机制
ngx_http_core_run_phases:
ngx_http_request_t 里的成员 phase_handler 标记了当前处理过程中所在阶段的 handler 序号,从 0 开始,通常是 NGX_HTTP_SERVER_REWRITE_PHASE 的第一个模块 ngx_http_rewrite_module。
在接收完请求头后,nginx 开始调用函数 ngx_http_core_run_phases 执行引擎。
启动引擎数组处理请求,从phase_handler的位置开始调用模块处理。
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;
// 得到core main配置
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
// 获取引擎里的handler数组
ph = cmcf->phase_engine.handlers;
// 从phase_handler的位置开始调用模块处理
// 外部请求的引擎数组起始序号是0,从头执行引擎数组,即先从Post read开始
// 内部请求,即子请求.跳过post read,直接从server rewrite开始执行,即查找server
while (ph[r->phase_handler].checker) {
// 调用引擎数组里的checker
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
// checker会检查handler的返回值
// 如果handler返回again/done那么就返回ok
// 退出引擎数组的处理
// 由于r->write_event_handler = ngx_http_core_run_phases
// 当再有写事件时会继续从之前的模块执行
// 如果checker返回again,那么继续在引擎数组里执行
// 模块由r->phase_handler序号指定,可能会有阶段的跳跃
if (rc == NGX_OK) {
return;
}
}
}
不同阶段的 checker 流程大同小异。
看下NGX_HTTP_CONTENT_PHASE 的 ngx_http_core_content_phase()源码。
ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
size_t root;
ngx_int_t rc;
ngx_str_t path;
// 检查请求是否有handler,也就是location里定义了handler
if (r->content_handler) {
// 设置写事件为ngx_http_request_empty_handler。
// 暂时不再进入ngx_http_core_run_phases。
// 这是因为内容产生阶段已经是“最后”一个阶段了,不需要再走其他阶段。
// 之后发送数据时会改为ngx_http_set_write_handler。
// 但我们也可以修改,让写事件触发我们自己的回调。
r->write_event_handler = ngx_http_request_empty_handler;
// 调用location专用的内容处理handler。
// 返回值传递给ngx_http_finalize_request。
// 相当于处理完后结束请求。
// 这种用法简化了客户代码,相当于模板方法模式。
// rc = handler(r); ngx_http_finalize_request(rc);
// 结束请求
// 但如果count>1,则不会真正结束
// handler可能返回done、again,例如调用read body
ngx_http_finalize_request(r, r->content_handler(r));
// 需要在之后的处理函数里继续处理,不能调用write_event_handler
return NGX_OK;
}
// 没有专门的handler
// 调用每个模块自己的处理函数
rc = ph->handler(r);
if (rc != NGX_DECLINED) {
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
// 模块handler返回decline,需要继续执行本阶段的下一个 handler。
ph++;
// 下一个模块有 checker
if (ph->checker) {
// 索引加1
r->phase_handler++;
// again继续引擎数组的循环
return NGX_AGAIN;
}
// 已经到了引擎数组的最末尾
// 没有一个content模块可以处理
// 结束引擎数组的循环
return NGX_OK;
}
日志处理阶段
日志处理不在 ngx_http_core_run_phases 里调用,而是在请求完毕时调用。
请求已经结束,调用log模块记录日志,在ngx_http_finalize_request和ngx_http_free_request里调用。
static void ngx_http_log_request(ngx_http_request_t *r)
{
ngx_uint_t i, n;
ngx_http_handler_pt *log_handler;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
// log handler不在引擎数组里
log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;
n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts;
// 不检查handler的返回值,直接调用,不使用checker
for (i = 0; i < n; i++) {
log_handler[i](r);
}
}
日志阶段是在给出应答后才进行调用,log配置解析。
static ngx_int_t ngx_http_log_init(ngx_conf_t *cf)
{
ngx_str_t *value;
ngx_array_t a;
ngx_http_handler_pt *h;
ngx_http_log_fmt_t *fmt;
ngx_http_log_main_conf_t *lmcf;
ngx_http_core_main_conf_t *cmcf;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
if (lmcf->combined_used) {
if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) {
return NGX_ERROR;
}
value = ngx_array_push(&a);
if (value == NULL) {
return NGX_ERROR;
}
*value = ngx_http_combined_fmt;
fmt = lmcf->formats.elts;
if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)
!= NGX_CONF_OK)
{
return NGX_ERROR;
}
}
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_log_handler;
return NGX_OK;
}
来源:freebuf.com 2020-12-12 19:08:38 by: stan1y
请登录后发表评论
注册