咱們知道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;
}
}
}