咱们知道nginx不少功能都是经过filter模块来实现的,如:替换content的sub module、content压缩的gzip module等。接下去咱们看看nginx是怎样处理filter模块的。 nginx
Nginx filter module全部的代码都在src\http\module\目录中,打开能够看到nginx拥有十几个filter module,以xxx_filter_module.c结尾的源文件即是filter module。 数组
Http请求分为Header、Content两部分,粗略的看代码能够发现,nginx对Http Header、Content使用了分别的filter函数进行处理。举个例子,咱们看gzip filter module的代码能够发现,ngx_http_gzip_header_filter函数即是nginx gzip对Http Header的处理,一样ngx_http_gzip_body_filter函数即是对Http Content的处理了。 memcached
nginx调用这十几个filter函数的流程即是本节内容所要讨论的。 函数
在源码配置的过程当中,会生成/objs/ngx_modules.c文件,其中定义了一个名为ngx_modules的数组,其中的内容即是nginx全部的module,源码为: post
ngx_module_t *ngx_modules[] = { ui
………… spa
&ngx_http_proxy_module, 指针
&ngx_http_memcached_module, server
&ngx_http_upstream_ip_hash_module, 索引
&ngx_http_write_filter_module,
&ngx_http_header_filter_module,
&ngx_http_chunked_filter_module,
&ngx_http_range_header_filter_module,
&ngx_http_gzip_filter_module,
&ngx_http_postpone_filter_module,
&ngx_http_ssi_filter_module,
&ngx_http_charset_filter_module,
&ngx_http_sub_filter_module,
&ngx_http_userid_filter_module,
&ngx_http_headers_filter_module,
&ngx_http_copy_filter_module,
&ngx_http_range_body_filter_module,
&ngx_http_not_modified_filter_module,
NULL
};
细心的朋友可能会发现,从ngx_http_write_filter_module开始,下面的全为filter_module结尾,没错,下面以filter_module结尾的即是nginx filter module。
咱们首先经过较熟悉的gzip filter module入手,咱们先看init函数ngx_http_gzip_filter_init,源码为:
static ngx_int_t
ngx_http_gzip_filter_init(ngx_conf_t *cf)
{
// 将 gzip head filter 串到整个链表的头部
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_gzip_header_filter;
// 将 gzip body filter 串到整个链表的头部
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_gzip_body_filter;
return NGX_OK;
}
代码其实挺简单的,作了两个链表串接操做。咱们先看header,ngx_http_top_header_filter为指向整个链表头结点的函数指针(注意:此处为函数指针),此处将next指针指向头指针,将头指针指向gzip header filter,而后在函数ngx_http_gzip_header_filter处理完成后,最终又会调用ngx_http_next_header_filter,这样就将gzip header filter结点串到链表的前面了。Body 处理过程相似,不予重复。
从如上代码分析能够看出,filter module都是串在链表前面的,因此ngx_modules数组中ngx_http_not_modified_filter_module应该是最后一个被串入的,也就是说,此模块应该在链表的最前面。
到此为止,咱们知道nginx全部filter module都是经过链表串接起来的,而且数组中最后面的元素在链表的第一个结点中。接下去咱们看看nginx怎样将整个链表执行起来。
ngx_http_core_module.c中ngx_http_send_header即是整个filter链的开始,在其中调用了ngx_http_top_header_filter函数,来启动整个header filter。一样ngx_http_output_filter即是启动整个body filter的函数。当整个head链处理完成以后,再行处理body链。
咱们经过nginx head filter中第一个被处理的filter来仔细分析一下。因为是逆向串入的,因此ngx_http_not_modified_filter_module就是第一个被处理的filter了。
咱们仍是先看init函数ngx_http_not_modified_filter_init,源码为:
static ngx_int_t
ngx_http_not_modified_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_not_modified_header_filter;
return NGX_OK;
}
很是简单,仅仅将head filter模块串入,此模块没有body filter,此处分析就先略过去了,正好简化分析。
咱们再看刚才初始化后的入口函数ngx_http_not_modified_header_filter,源码为:
static ngx_int_t
ngx_http_not_modified_header_filter(ngx_http_request_t *r)
{
if (r->headers_out.status != NGX_HTTP_OK
|| r != r->main
|| r->headers_out.last_modified_time == -1)
{
return ngx_http_next_header_filter(r);
}
// 判断是否应该返回 Http 412(Precondition Failed)
if (r->headers_in.if_unmodified_since) {
return ngx_http_test_precondition(r);
}
// 判断是否应该返回 Http 304(Not Modified)
if (r->headers_in.if_modified_since) {
return ngx_http_test_not_modified(r);
}
return ngx_http_next_header_filter(r);
}
代码也挺简单,首先判断Http状态、一些Flag等信息,若是没有经过判断规则,则直接跳入下一个head filter。而后判断是否没达到返回条件,即:nginx是否应该返回Http 412(Precondition Failed)状态码。而后判断是否应该返回Http 304(Not Modified)状态码。最终若是都没判断经过的话,则直接跳入下一个head filter的处理了。
咱们作个假设r->headers_in.if_modify_since不为NULL,则进入ngx_http_test_not_modified,在此函数中,若是执行成功,则将http状态值设置为304,最终跳到下一个head filter。
回过头来再看看,因为此filter仅仅处理Http head信息,无需处理任何Http Content,因此固然就没有相似 body_filter的函数了。
咱们接下去看第二个将要处理的模块,即ngx_modules中的倒数第二项。在处理倒数第二项时,其仅仅对Http Content作了处理,处理的流程相似于前面的head分析,此处就不详细解释了。
最终,咱们能够看一下ngx_http_header_filter_module,nginx filter module对Http Header的最后处理。先看init函数ngx_http_header_filter_init,因为这个是第一个结点,在这以前并不存在head filter结点,因此仅仅须要赋值ngx_http_top_header_filter便可。在此head filter中,最终会调用ngx_http_write_filter函数(在ngx_http_header_filter之中)完成整个nginx head filter的处理。
ngx_http_writer_filter_module最终对Http body进行处理,最终会调用send_chain函数(ngx_http_write_filter)完成整个nginx body filter的处理。
到此为止,咱们解释了整个filter module链的串接过程,也作出了实例分析。但有个问题一直不曾解释,nginx是怎样调用全部filter module的init函数的,由于查看代码知道,每一个filter的init函数名不相同,filter module也仅仅存在于一个c文件中,并没有对应的头文件。
本篇开头,咱们列出过一个结构体,其中包含了全部nginx module的指针,而对应于ngx_http_not_modified_filter_module的是最后一个,此结构体最终的定义在ngx_http_not_modified_filter_module.c文件中,源码为:
ngx_module_t ngx_http_not_modified_filter_module = {
NGX_MODULE_V1,
&ngx_http_not_modified_filter_module_ctx, /* module context */
NULL, /* 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_module_t 的定义:
typedef struct ngx_module_s ngx_module_t;
struct ngx_module_s {
ngx_uint_t ctx_index;
ngx_uint_t index;
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t spare2;
ngx_uint_t spare3;
ngx_uint_t version;
void *ctx;
ngx_command_t *commands;
ngx_uint_t type;
ngx_int_t (*init_master)(ngx_log_t *log);
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle);
void (*exit_master)(ngx_cycle_t *cycle);
uintptr_t spare_hook0;
uintptr_t spare_hook1;
uintptr_t spare_hook2;
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};
能够看到,最终启用的ngx_module_t的数据项为 ngx_module_t[index]::ctx(注意NGX_MODULE_V1是一个宏,对应前面7项),此ctx定义为void*类型,在此filter module中,最终转化为类型ngx_http_module_t,其中的数据为:
typedef struct {
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
void *(*create_main_conf)(ngx_conf_t *cf);
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
void *(*create_srv_conf)(ngx_conf_t *cf);
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
void *(*create_loc_conf)(ngx_conf_t *cf);
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
static ngx_http_module_t ngx_http_not_modified_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_not_modified_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
至此咱们看到了ngx_http_not_modified_filter_init函数,也就是说,这个函数最终能够经过ngx_modules数组来访问并调用,示例代码:
ngx_modules[module index]->ctx->postconfiguration(cf);
如上module index假设为ngx_http_not_modified_filter_module的数组索引,ngx_modules[module index]->ctx即是ngx_http_not_modified_filter_module的ctx,即ngx_http_not_modified_filter_module_ctx。ngx_modules[module index]->ctx的类型为ngx_http_module_t,能够经过预约义类型来进行访问,因此ngx_modules[module index]->ctx->postconfiguration(cf);便调用到了最终的init函数。
在ngx_http.c中有函数ngx_http_block,其中便如上进行调用了,源码片断:
// 循环遍历整个 ngx_modules 数组
for (m = 0; ngx_modules[m]; m++) {
// 对 module 的类型进行判断
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
// 临时时变量 module 指向 ngx_modules[m]->ctx,即 xxx_module_filter_ctx 函数
// 对于 ngx_http_not_modified_filter_module 来讲,module 即 ngx_http_not_modified_filter_module_ctx
module = ngx_modules[m]->ctx;
// 判断并调用 postconfiguration 函数,即 xxx_module_filter_init 函数
// 对于 ngx_http_not_modified_filter_module 来讲,即调用了 ngx_http_not_modified_filter_init
if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}