Nginx filter 模塊解析

Nginx filter 模塊解析

我們知道nginx很多功能都是通過filter模塊來實現的,如:替換contentsub modulecontent壓縮的gzip module等。接下去我們看看nginx是怎樣處理filter模塊的。

Nginx filter module所有的代碼都在src\http\module\目錄中,打開可以看到nginx擁有十幾個filter module,以xxx_filter_module.c結尾的源文件便是filter module

Http請求分爲HeaderContent兩部分,粗略的看代碼可以發現,nginxHttp HeaderContent使用了分別的filter函數進行處理。舉個例子,我們看gzip filter module的代碼可以發現,ngx_http_gzip_header_filter函數便是nginx gzipHttp Header的處理,同樣ngx_http_gzip_body_filter函數便是對Http Content的處理了。

nginx調用這十幾個filter函數的流程便是本節內容所要討論的。

 

在源碼配置的過程中,會生成/objs/ngx_modules.c文件,其中定義了一個名爲ngx_modules的數組,其中的內容便是nginx所有的module,源碼爲:

ngx_module_t *ngx_modules[] = {

    …………

    &ngx_http_proxy_module,

    &ngx_http_memcached_module,

    &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;

}

    代碼其實挺簡單的,做了兩個鏈表串接操作。我們先看headerngx_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.cngx_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 412Precondition Failed

    if (r->headers_in.if_unmodified_since) {

        return ngx_http_test_precondition(r);

    }

 

    // 判斷是否應該返回 Http 304Not 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 412Precondition Failed)狀態碼。然後判斷是否應該返回Http 304Not 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_modulenginx filter moduleHttp 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 moduleinit函數的,因爲查看代碼知道,每個filterinit函數名不相同,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_modulectx,即ngx_http_not_modified_filter_module_ctxngx_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;

            }

        }

    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章