nginx源码分析——http模块
源码:nginx 1.12.0
⼀、nginx http模块简介
由于nginx的性能优势,现在已经有越来越多的单位、个⼈采⽤nginx或者openresty、tengine等衍⽣版来作为WEB服务器、负载均衡服务器、安全⽹关来使⽤。在这些场景下,依赖的就是nginx的http模块,nginx的设计者采⽤模块化的设计思路,允许⽤户在http请求处理的各个阶段添加⾃⼰设计的模块来实现⾃⼰的⼀些逻辑,扩充⼀些功能。
⼆、http模块功能介绍
http模块的丰富功能其实是由⼀个个http_module来共同实现的,每个module提供单独的功能⽅便开发、维护。nginx通过配置⽂件中的⼀个个配置项的设置来实现对相关模块功能的调⽤,如下⽰例:
http {
server {
nginx和网关怎么配合使用location ~ \.php$ {
#proxy_pass命令在http_proxy_module中的被ngx_http_proxy_pass函数实现
proxy_pass 127.0.0.1;
}
}
}
http模块对⼀个完整http请求的处理流程如下:
上⾯的流程中,http_handler、output_filter这两步分别是执⾏phase_handler、filter两类http module的位置。在讲这两类模块之前,需要先理清这些模块是如何加载到添加到nginx中的。
在nginx启动时,会将各个module加载到⼀个数组中,然后调⽤ngx_parse_conf寻配置⽂件中特定类型的command,然后根据command到包含该command的module,并执⾏command对应的函数(这些函数通常是将对应command的配置结构中设置⼀些变量或者将函数赋值给相关的handler函数指针,以便在处理对应command请求时可以直接调⽤)。
在处理http这个command时,对应函数会初始化所有类型为HTTP_CORE_MODULE的模块,处理流程如下:
三、phase handlers请求处理阶段
nginx对http的请求处理分成了POST_READ, SERVER_REWRITE, FIND_CONFIG, REWRITE, POST_REWRITE, PREACCESS, ACCESS, POST_ACCESS, TRY_FILES, CONTENT, LOG这11个阶段,除了FIND_CONFIG, POST_REWRITE, POST_ACCESS,
TRY_FILES这4个阶段⽤户不能添加⾃定义的处理函数,其余每个阶段都通过ngx_http_init_phases函数初始化了⼀个数组⽤于保存本阶段的处理函数指针。如下:
//  nginx/src/http/ngx_http.c
static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
//申请指针数组,⽤于存储该阶段的处理函数指针
if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)  {
return NGX_ERROR;
}
.....
return NGX_OK;
}
/ nginx/src/http/modules/ngx_http_rewrite_module.c /
// 该函数是在rewrite的postconfiguration阶段被调⽤
static ngx_int_t
ngx_http_rewrite_init(ngx_conf_t *cf)
{
{
.....
//获取rewrite命令对应的core_module配置结构
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
//在&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers指向的数组中申请⼀个函数指针空间
h = ngx_array_push(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
//将函数添加到数组中新申请的元素中
*h = ngx_http_rewrite_handler;
 ......
return NGX_OK;
}
//  nginx/src/http/ngx_http.c
//遍历各个PHASE,给每个PHASE添加相关的checker函数,并将各个phase阶段注册的函数按照phase+数组index的顺序统⼀添加到⼀个phase_engine中,每个phas static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
h = cmcf->phases[i].handlers.elts;
switch (i) {
case NGX_HTTP_SERVER_REWRITE_PHASE:
if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
cmcf->phase_engine.server_rewrite_index = n;
}
//根据phase阶段确定每个阶段对应的checker
checker = ngx_http_core_rewrite_phase;
break;
.....
.....
default:
checker = ngx_http_core_generic_phase;
}
n += cmcf->phases[i].lts;
//遍历各个phase handlers数组中的注册函数并统⼀添加到cmcf->phase_engine.handlers中
for (j = cmcf->phases[i].lts - 1; j >=0; j--) {
ph->checker = checker;
ph->handler = h[j];
ph->next = n;
ph++;
}
}
return NGX_OK;
}
  在查看module源码的时候发现⼀些没有注册postconfiguration函数的模块,也就是说这些模块并没有通过ngx_array_push函数将
handler添加到对应的phase中,例如http_memcached_module。但是这些模块通过command对应的函数将真正的handler赋值给了该command所在location对应的conf结构的handler指针,这些handler指针在ngx_http_update_location_config函数(该函数被
ngx_http_core_find_config_phase调⽤)中被赋值给了r->content_handler,然后在http_core_content_phase函数中被执⾏了(但是
也跳过了该phase中其他的handler)。由此可见,这些没有通过ngx_array_push显⽰的加⼊到某⼀phase的handler,都通过这种⽅式加
⼊到了content_phase中。
void
ngx_http_update_location_config(ngx_http_request_t *r)
{
ngx_http_core_loc_conf_t  *clcf;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
....
//如果对应command存在直接的handler函数,就直接调⽤
if (clcf->handler) {
r->content_handler = clcf->handler;
}
}
ngx_int_t
ngx_http_core_content_phase(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph)
{
....
if (r->content_handler) {
r->write_event_handler = ngx_http_request_empty_handler;
//如果存在的话就直接执⾏该handler函数并跳过content阶段的其他处理函数
ngx_http_finalize_request(r, r->content_handler(r));
return NGX_OK;
}
.....
}
//调⽤各个所有phase中的函数完成对request的处理
void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
....
while (ph[r->phase_handler].checker) {
//根据请求中的phase索引确定执⾏的checker函数
/
/checker函数根据处理结果来决定是结束处理还是继续下⼀个phase handler
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) {
return;
}
}
}
  综合上述两种request处理module处理⽅法,如果需要在特定的PHASE阶段执⾏的话,就⽤ngx_array_push这种⽅式;r-
>content_handler这种⽅式只能在CONTENT PHASE阶段执⾏,且导致该阶段的其他handler⽆法执⾏,这需要开发者考虑。
四、filter输出处理模块
filter输出处理主要是在输出的时候对输出内容进⾏处理,filter handler调⽤顺序与phase handler类型(根据phase阶段顺序)不同,filter类型的各个handler是通过链表的形式联系到⼀块的,只有链表头ngx_http_top_body_filter 函数指针是全局的。在函数
ngx_http_output_filter中,有⼀个调⽤filter handler的⼊⼝,这个函数可以在正常的ngx_http_send_response函数中被调⽤,也可以在特定的phase handler中被调⽤。
在各个module初始化的时候,会将ngx_http_top_body_filter指针的值保存到ngx_http_next_body_filter局部变量中,然后把当前filter的处理函数赋值给ngx_http_top_body_filter,同时每个filter的处理函数中都将ngx_http_next_body_filter的值赋值给ctx-
>output_filter,以便可以顺序遍历各个filter。
对于ngx_http_write_filter_module、ngx_http_header_filter_module两个模块中没有ngx_http_next_body_filter变量,是因为ngx_http_write_filter_module是最后⼀个filter模块,因此不⽤next。ngx_http_header_filter_module虽然是倒数第⼆个模块,但是filter函数中调⽤了write_filter的函数,因此也没有使⽤next。
ngx_int_t
ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
.....
//通过链表接⼝来⼀次遍历各个filter模块进⾏处理
rc = ngx_http_top_body_filter(r, in);
.....
return rc;
}
/ nginx/src/http/ngx_http_copy_filter_module.c //
//在postconfiguration阶段被调⽤,初始化filter handler链表
static ngx_int_t
ngx_http_copy_filter_init(ngx_conf_t *cf)
{
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_copy_filter;
return NGX_OK;
}
//将本阶段输出执⾏链表中下⼀个filter handler函数
static ngx_int_t
ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
.....
.
....
ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module);
//记录下⼀个要执⾏的filter
ctx->output_filter = (ngx_output_chain_filter_pt) ngx_http_next_body_filter;        ctx->filter_ctx = r;
....
....
rc = ngx_output_chain(ctx, in);
.....
return rc;
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。