在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Linux/ 模塊的基本結(jié)構(gòu)
示例: hello handler 模塊
什么是 Nginx
handler 模塊的掛載
Nginx 特點
handler 模塊簡介
初探 Nginx 架構(gòu)
Nginx 的模塊化體系結(jié)構(gòu)
更多 handler 模塊示例分析
Nginx 基礎(chǔ)概念
upstream 模塊簡介
Nginx 的請求處理
過濾模塊簡介
基本數(shù)據(jù)結(jié)構(gòu)
模塊的基本結(jié)構(gòu)
負載均衡模塊
過濾模塊的分析
core 模塊
handler 模塊的基本結(jié)構(gòu)
Nginx 的配置系統(tǒng)
handler 的編寫步驟
handler 模塊的編譯和使用
event 模塊

模塊的基本結(jié)構(gòu)

在這一節(jié)我們將會對通常的模塊開發(fā)過程中,每個模塊所包含的一些常用的部分進行說明。這些部分有些是必須的,有些不是必須的。同時這里所列出的這些東西對于其他類型的模塊,例如 filter 模塊等也都是相同的。

模塊配置結(jié)構(gòu)

基本上每個模塊都會提供一些配置指令,以便于用戶可以通過配置來控制該模塊的行為。那么這些配置信息怎么存儲呢?那就需要定義該模塊的配置結(jié)構(gòu)來進行存儲。

大家都知道 Nginx 的配置信息分成了幾個作用域(scope,有時也稱作上下文),這就是 main,server 以及 location。同樣的每個模塊提供的配置指令也可以出現(xiàn)在這幾個作用域里。那對于這三個作用域的配置信息,每個模塊就需要定義三個不同的數(shù)據(jù)結(jié)構(gòu)去進行存儲。當(dāng)然,不是每個模塊都會在這三個作用域都提供配置指令的。那么也就不一定每個模塊都需要定義三個數(shù)據(jù)結(jié)構(gòu)去存儲這些配置信息了。視模塊的實現(xiàn)而言,需要幾個就定義幾個。

有一點需要特別注意的就是,在模塊的開發(fā)過程中,我們最好使用 Nginx 原有的命名習(xí)慣。這樣跟原代碼的契合度更高,看起來也更舒服。

對于模塊配置信息的定義,命名習(xí)慣是ngx_http_<module name>_(main|srv|loc)_conf_t。這里有個例子,就是從我們后面將要展示給大家的 hello module 中截取的。

    typedef struct
    {
        ngx_str_t hello_string;
        ngx_int_t hello_counter;
    }ngx_http_hello_loc_conf_t;

模塊配置指令

一個模塊的配置指令是定義在一個靜態(tài)數(shù)組中的。同樣地,我們來看一下從 hello module 中截取的模塊配置指令的定義。

    static ngx_command_t ngx_http_hello_commands[] = {
       { 
            ngx_string("hello_string"),
            NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,
            ngx_http_hello_string,
            NGX_HTTP_LOC_CONF_OFFSET,
            offsetof(ngx_http_hello_loc_conf_t, hello_string),
            NULL },

        { 
            ngx_string("hello_counter"),
            NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
            ngx_http_hello_counter,
            NGX_HTTP_LOC_CONF_OFFSET,
            offsetof(ngx_http_hello_loc_conf_t, hello_counter),
            NULL },               

        ngx_null_command
    };

其實看這個定義,就基本能看出來一些信息。例如,我們是定義了兩個配置指令,一個是叫 hello_string,可以接受一個參數(shù),或者是沒有參數(shù)。另外一個命令是 hello_counter,接受一個 NGX_CONF_FLAG 類型的參數(shù)。除此之外,似乎看起來有點迷惑。沒有關(guān)系,我們來詳細看一下 ngx_command_t,一旦我們了解這個結(jié)構(gòu)的詳細信息,那么我相信上述這個定義所表達的所有信息就不言自明了。

ngx_command_t 的定義,位于src/core/ngx_conf_file.h中。

    struct ngx_command_s {
        ngx_str_t             name;
        ngx_uint_t            type;
        char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
        ngx_uint_t            conf;
        ngx_uint_t            offset;
        void                 *post;
    };

name: 配置指令的名稱。

type: 該配置的類型,其實更準(zhǔn)確一點說,是該配置指令屬性的集合。Nginx 提供了很多預(yù)定義的屬性值(一些宏定義),通過邏輯或運算符可組合在一起,形成對這個配置指令的詳細的說明。下面列出可在這里使用的預(yù)定義屬性值及說明。

  • NGX_CONF_NOARGS:配置指令不接受任何參數(shù)。
  • NGX_CONF_TAKE1:配置指令接受 1 個參數(shù)。
  • NGX_CONF_TAKE2:配置指令接受 2 個參數(shù)。
  • NGX_CONF_TAKE3:配置指令接受 3 個參數(shù)。
  • NGX_CONF_TAKE4:配置指令接受 4 個參數(shù)。
  • NGX_CONF_TAKE5:配置指令接受 5 個參數(shù)。
  • NGX_CONF_TAKE6:配置指令接受 6 個參數(shù)。
  • NGX_CONF_TAKE7:配置指令接受 7 個參數(shù)。

可以組合多個屬性,比如一個指令即可以不填參數(shù),也可以接受1個或者2個參數(shù)。那么就是NGX_CONF_NOARGS|NGX_CONF_TAKE1|NGX_CONF_TAKE2。如果寫上面三個屬性在一起,你覺得麻煩,那么沒有關(guān)系,Nginx 提供了一些定義,使用起來更簡潔。

  • NGX_CONF_TAKE12:配置指令接受 1 個或者 2 個參數(shù)。
  • NGX_CONF_TAKE13:配置指令接受 1 個或者 3 個參數(shù)。
  • NGX_CONF_TAKE23:配置指令接受 2 個或者 3 個參數(shù)。
  • NGX_CONF_TAKE123:配置指令接受 1 個或者 2 個或者 3 參數(shù)。
  • NGX_CONF_TAKE1234:配置指令接受 1 個或者 2 個或者 3 個或者 4 個參數(shù)。
  • NGX_CONF_1MORE:配置指令接受至少一個參數(shù)。
  • NGX_CONF_2MORE:配置指令接受至少兩個參數(shù)。
  • NGX_CONF_MULTI: 配置指令可以接受多個參數(shù),即個數(shù)不定。
  • NGX_CONF_BLOCK:配置指令可以接受的值是一個配置信息塊。也就是一對大括號括起來的內(nèi)容。里面可以再包括很多的配置指令。比如常見的 server 指令就是這個屬性的。
  • NGX_CONF_FLAG:配置指令可以接受的值是"on"或者"off",最終會被轉(zhuǎn)成 bool 值。
  • NGX_CONF_ANY:配置指令可以接受的任意的參數(shù)值。一個或者多個,或者"on"或者"off",或者是配置塊。

最后要說明的是,無論如何,Nginx 的配置指令的參數(shù)個數(shù)不可以超過 NGX_CONF_MAX_ARGS 個。目前這個值被定義為 8,也就是不能超過 8 個參數(shù)值。

下面介紹一組說明配置指令可以出現(xiàn)的位置的屬性。

  • NGX_DIRECT_CONF:可以出現(xiàn)在配置文件中最外層。例如已經(jīng)提供的配置指令 daemon,master_process 等。
  • NGX_MAIN_CONF: http、mail、events、error_log 等。
  • NGX_ANY_CONF: 該配置指令可以出現(xiàn)在任意配置級別上。

對于我們編寫的大多數(shù)模塊而言,都是在處理http相關(guān)的事情,也就是所謂的都是NGX_HTTP_MODULE,對于這樣類型的模塊,其配置可能出現(xiàn)的位置也是分為直接出現(xiàn)在http里面,以及其他位置。

  • NGX_HTTP_MAIN_CONF: 可以直接出現(xiàn)在 http 配置指令里。
  • NGX_HTTP_SRV_CONF: 可以出現(xiàn)在 http 里面的 server 配置指令里。
  • NGX_HTTP_LOC_CONF: 可以出現(xiàn)在 http server 塊里面的 location 配置指令里。
  • NGX_HTTP_UPS_CONF: 可以出現(xiàn)在 http 里面的 upstream 配置指令里。
  • NGX_HTTP_SIF_CONF: 可以出現(xiàn)在 http 里面的 server 配置指令里的 if 語句所在的 block 中。
  • NGX_HTTP_LMT_CONF: 可以出現(xiàn)在 http 里面的 limit_except 指令的 block 中。
  • NGX_HTTP_LIF_CONF: 可以出現(xiàn)在 http server 塊里面的 location 配置指令里的 if 語句所在的 block 中。

set: 這是一個函數(shù)指針,當(dāng) Nginx 在解析配置的時候,如果遇到這個配置指令,將會把讀取到的值傳遞給這個函數(shù)進行分解處理。因為具體每個配置指令的值如何處理,只有定義這個配置指令的人是最清楚的。來看一下這個函數(shù)指針要求的函數(shù)原型。

char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

先看該函數(shù)的返回值,處理成功時,返回 NGX_OK,否則返回 NGX_CONF_ERROR 或者是一個自定義的錯誤信息的字符串。

再看一下這個函數(shù)被調(diào)用的時候,傳入的三個參數(shù)。

  • cf: 該參數(shù)里面保存從配置文件讀取到的原始字符串以及相關(guān)的一些信息。特別注意的是這個參數(shù)的args字段是一個 ngx_str_t類型的數(shù)組,該數(shù)組的首個元素是這個配置指令本身,第二個元素是指令的第一個參數(shù),第三個元素是第二個參數(shù),依次類推。

  • cmd: 這個配置指令對應(yīng)的 ngx_command_t 結(jié)構(gòu)。

  • conf: 就是定義的存儲這個配置值的結(jié)構(gòu)體,比如在上面展示的那個 ngx_http_hello_loc_conf_t。當(dāng)解析這個 hello_string 變量的時候,傳入的 conf 就指向一個 ngx_http_hello_loc_conf_t 類型的變量。用戶在處理的時候可以使用類型轉(zhuǎn)換,轉(zhuǎn)換成自己知道的類型,再進行字段的賦值。

為了更加方便的實現(xiàn)對配置指令參數(shù)的讀取,Nginx 已經(jīng)默認提供了對一些標(biāo)準(zhǔn)類型的參數(shù)進行讀取的函數(shù),可以直接賦值給 set 字段使用。下面來看一下這些已經(jīng)實現(xiàn)的 set 類型函數(shù)。

  • ngx_conf_set_flag_slot: 讀取 NGX_CONF_FLAG 類型的參數(shù)。
  • ngx_conf_set_str_slot:讀取字符串類型的參數(shù)。
  • ngx_conf_set_str_array_slot: 讀取字符串?dāng)?shù)組類型的參數(shù)。
  • ngx_conf_set_keyval_slot: 讀取鍵值對類型的參數(shù)。
  • ngx_conf_set_num_slot: 讀取整數(shù)類型(有符號整數(shù) ngx_int_t)的參數(shù)。
  • ngx_conf_set_size_slot:讀取 size_t 類型的參數(shù),也就是無符號數(shù)。
  • ngx_conf_set_off_slot: 讀取 off_t 類型的參數(shù)。
  • ngx_conf_set_msec_slot: 讀取毫秒值類型的參數(shù)。
  • ngx_conf_set_sec_slot: 讀取秒值類型的參數(shù)。
  • ngx_conf_set_bufs_slot: 讀取的參數(shù)值是 2 個,一個是 buf 的個數(shù),一個是 buf 的大小。例如: output_buffers 1 128k;
  • ngx_conf_set_enum_slot: 讀取枚舉類型的參數(shù),將其轉(zhuǎn)換成整數(shù) ngx_uint_t 類型。
  • ngx_conf_set_bitmask_slot: 讀取參數(shù)的值,并將這些參數(shù)的值以 bit 位的形式存儲。例如:HttpDavModule 模塊的 dav_methods 指令。

conf: 該字段被 NGX_HTTP_MODULE 類型模塊所用 (我們編寫的基本上都是 NGX_HTTP_MOUDLE,只有一些 Nginx 核心模塊是非 NGX_HTTP_MODULE),該字段指定當(dāng)前配置項存儲的內(nèi)存位置。實際上是使用哪個內(nèi)存池的問題。因為 http 模塊對所有 http 模塊所要保存的配置信息,劃分了 main, server 和 location 三個地方進行存儲,每個地方都有一個內(nèi)存池用來分配存儲這些信息的內(nèi)存。這里可能的值為 NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET 或 NGX_HTTP_LOC_CONF_OFFSET。當(dāng)然也可以直接置為 0,就是 NGX_HTTP_MAIN_CONF_OFFSET。

offset: 指定該配置項值的精確存放位置,一般指定為某一個結(jié)構(gòu)體變量的字段偏移。因為對于配置信息的存儲,一般我們都是定義個結(jié)構(gòu)體來存儲的。那么比如我們定義了一個結(jié)構(gòu)體 A,該項配置的值需要存儲到該結(jié)構(gòu)體的 b 字段。那么在這里就可以填寫為 offsetof(A, b)。對于有些配置項,它的值不需要保存或者是需要保存到更為復(fù)雜的結(jié)構(gòu)中時,這里可以設(shè)置為 0。

post: 該字段存儲一個指針??梢灾赶蛉魏我粋€在讀取配置過程中需要的數(shù)據(jù),以便于進行配置讀取的處理。大多數(shù)時候,都不需要,所以簡單地設(shè)為 0 即可。

看到這里,應(yīng)該就比較清楚了。ngx_http_hello_commands 這個數(shù)組每 5 個元素為一組,用來描述一個配置項的所有情況。那么如果有多個配置項,只要按照需要再增加 5 個對應(yīng)的元素對新的配置項進行說明。

需要注意的是,就是在ngx_http_hello_commands這個數(shù)組定義的最后,都要加一個ngx_null_command作為結(jié)尾。

模塊上下文結(jié)構(gòu)

這是一個 ngx_http_module_t 類型的靜態(tài)變量。這個變量實際上是提供一組回調(diào)函數(shù)指針,這些函數(shù)有在創(chuàng)建存儲配置信息的對象的函數(shù),也有在創(chuàng)建前和創(chuàng)建后會調(diào)用的函數(shù)。這些函數(shù)都將被 Nginx 在合適的時間進行調(diào)用。

    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; 
  • preconfiguration: 在創(chuàng)建和讀取該模塊的配置信息之前被調(diào)用。

  • postconfiguration: 在創(chuàng)建和讀取該模塊的配置信息之后被調(diào)用。

  • create_main_conf: 調(diào)用該函數(shù)創(chuàng)建本模塊位于 http block 的配置信息存儲結(jié)構(gòu)。該函數(shù)成功的時候,返回創(chuàng)建的配置對象。失敗的話,返回 NULL。

  • init_main_conf: 調(diào)用該函數(shù)初始化本模塊位于 http block 的配置信息存儲結(jié)構(gòu)。該函數(shù)成功的時候,返回 NGX_CONF_OK。失敗的話,返回 NGX_CONF_ERROR 或錯誤字符串。

  • create_srv_conf: 調(diào)用該函數(shù)創(chuàng)建本模塊位于 http server block 的配置信息存儲結(jié)構(gòu),每個 server block 會創(chuàng)建一個。該函數(shù)成功的時候,返回創(chuàng)建的配置對象。失敗的話,返回 NULL。

  • merge_srv_conf: 因為有些配置指令既可以出現(xiàn)在 http block,也可以出現(xiàn)在 http server block 中。那么遇到這種情況,每個 server 都會有自己存儲結(jié)構(gòu)來存儲該 server 的配置,但是在這種情況下 http block 中的配置與 server block 中的配置信息發(fā)生沖突的時候,就需要調(diào)用此函數(shù)進行合并,該函數(shù)并非必須提供,當(dāng)預(yù)計到絕對不會發(fā)生需要合并的情況的時候,就無需提供。當(dāng)然為了安全起見還是建議提供。該函數(shù)執(zhí)行成功的時候,返回 NGX_CONF_OK。失敗的話,返回 NGX_CONF_ERROR 或錯誤字符串。

  • create_loc_conf: 調(diào)用該函數(shù)創(chuàng)建本模塊位于 location block 的配置信息存儲結(jié)構(gòu)。每個在配置中指明的 location 創(chuàng)建一個。該函數(shù)執(zhí)行成功,返回創(chuàng)建的配置對象。失敗的話,返回 NULL。

  • merge_loc_conf: 與 merge_srv_conf 類似,這個也是進行配置值合并的地方。該函數(shù)成功的時候,返回 NGX_CONF_OK。失敗的話,返回 NGX_CONF_ERROR 或錯誤字符串。

Nginx 里面的配置信息都是上下一層層的嵌套的,對于具體某個 location 的話,對于同一個配置,如果當(dāng)前層次沒有定義,那么就使用上層的配置,否則使用當(dāng)前層次的配置。

這些配置信息一般默認都應(yīng)該設(shè)為一個未初始化的值,針對這個需求,Nginx 定義了一系列的宏定義來代表各種配置所對應(yīng)數(shù)據(jù)類型的未初始化值,如下:

    #define NGX_CONF_UNSET       -1
    #define NGX_CONF_UNSET_UINT  (ngx_uint_t) -1
    #define NGX_CONF_UNSET_PTR   (void *) -1
    #define NGX_CONF_UNSET_SIZE  (size_t) -1
    #define NGX_CONF_UNSET_MSEC  (ngx_msec_t) -1

又因為對于配置項的合并,邏輯都類似,也就是前面已經(jīng)說過的,如果在本層次已經(jīng)配置了,也就是配置項的值已經(jīng)被讀取進來了(那么這些配置項的值就不會等于上面已經(jīng)定義的那些 UNSET 的值),就使用本層次的值作為定義合并的結(jié)果,否則,使用上層的值,如果上層的值也是這些UNSET類的值,那就賦值為默認值,否則就使用上層的值作為合并的結(jié)果。對于這樣類似的操作,Nginx 定義了一些宏操作來做這些事情,我們來看其中一個的定義。

    #define ngx_conf_merge_uint_value(conf, prev, default) \
        if (conf == NGX_CONF_UNSET_UINT) {      \
            conf = (prev == NGX_CONF_UNSET_UINT) ? default : prev; \
        }

顯而易見,這個邏輯確實比較簡單,所以其它的宏定義也類似,我們就列具其中的一部分吧。

    ngx_conf_merge_value
    ngx_conf_merge_ptr_value
    ngx_conf_merge_uint_value
    ngx_conf_merge_msec_value
    ngx_conf_merge_sec_value

等等。

下面來看一下 hello 模塊的模塊上下文的定義,加深一下印象。

    static ngx_http_module_t ngx_http_hello_module_ctx = {
        NULL,                          /* preconfiguration */
        ngx_http_hello_init,           /* postconfiguration */

        NULL,                          /* create main configuration */
        NULL,                          /* init main configuration */

        NULL,                          /* create server configuration */
        NULL,                          /* merge server configuration */

        ngx_http_hello_create_loc_conf, /* create location configuration */
        NULL                        /* merge location configuration */
    };

注意:這里并沒有提供 merge_loc_conf 函數(shù),因為我們這個模塊的配置指令已經(jīng)確定只出現(xiàn)在 NGX_HTTP_LOC_CONF 中這一個層次上,不會發(fā)生需要合并的情況。

模塊的定義

對于開發(fā)一個模塊來說,我們都需要定義一個 ngx_module_t 類型的變量來說明這個模塊本身的信息,從某種意義上來說,這是這個模塊最重要的一個信息,它告訴了 Nginx 這個模塊的一些信息,上面定義的配置信息,還有模塊上下文信息,都是通過這個結(jié)構(gòu)來告訴 Nginx 系統(tǒng)的,也就是加載模塊的上層代碼,都需要通過定義的這個結(jié)構(gòu),來獲取這些信息。

我們先來看下 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            abi_compatibility;
        ngx_uint_t            major_version;
        ngx_uint_t            minor_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;
    };

    #define NGX_NUMBER_MAJOR  3
    #define NGX_NUMBER_MINOR  1
    #define NGX_MODULE_V1          0, 0, 0, 0,                              \
        NGX_DSO_ABI_COMPATIBILITY, NGX_NUMBER_MAJOR, NGX_NUMBER_MINOR
    #define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0

再看一下 hello 模塊的模塊定義。

    ngx_module_t ngx_http_hello_module = {
        NGX_MODULE_V1,
        &ngx_http_hello_module_ctx,    /* module context */
        ngx_http_hello_commands,       /* 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
    };

模塊可以提供一些回調(diào)函數(shù)給 Nginx,當(dāng) Nginx 在創(chuàng)建進程線程或者結(jié)束進程線程時進行調(diào)用。但大多數(shù)模塊在這些時刻并不需要做什么,所以都簡單賦值為 NULL。