Nginx filter 模塊解析

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

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的處理了。 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;

}

    代碼其實挺簡單的,作了兩個鏈表串接操做。咱們先看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;

            }

        }

    }

相關文章
相關標籤/搜索