nginx处理http请求十一个阶段流程分析 – 作者:stan1y

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;

这个其实相当一个二维链表:

1606631169_5fc33f0111de6cdee3dbf.png!small?1606631166258

引擎处理

使用二维数组 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。

1606631143_5fc33ee765407fcea22a1.png!small?1606631140569

引擎运行机制

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

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论