解剖Nginx·模組開發篇(2)ngx_http_hello_world_module 模組基本結構定義

鍾超發表於2012-06-03

解剖Nginx·模組開發篇(2)ngx_http_hello_world_module 基本結構定義

  • 作者:柳大·Poechant(鍾超)
  • 郵箱:zhongchao.ustc#gmail.com(# -> @)
  • 部落格:Blog.CSDN.net/Poechant
  • 日期:June 2nd, 2012

HelloWorld 是一個典型的 location 模組。什麼是 location 模組?在 Nginx 中,根據作用域,有 main 模組、server 模組、location 模組。

1 模組定義

在 HelloWorld 模組中有一個 ngx_http_hello_world_module 變數,用於定義模組。它是 ngx_module_t 型別。ngx_module_t 是 ngx_module_s 的別名,其定義如下:

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; // Nginx模組版本

    void                 *ctx; // 上下文定義的地址
    ngx_command_t        *commands; // 命令定義地址
    ngx_uint_t            type; // 模組型別

    ngx_int_t           (*init_master)(ngx_log_t *log); // 初始化 master 時執行

    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); // 退出 master 時執行

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

在 HelloWorld 例子中:

// Structure for the HelloWorld module, the most important thing
ngx_module_t ngx_http_hello_world_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_world_module_ctx,
    ngx_http_hello_world_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

1.1 NGX_MODULE_V1

看它的定義你就知道,它是用來填充前 7 個 fields 的。

NGX_MODULE_V1          0, 0, 0, 0, 0, 0, 1

1.2 模組型別

我們的模組是 HTTP 模組,還可以開發 CORE 模組,或者 CONF 模組等等。

// ngx_http_config.h
#define NGX_HTTP_MODULE      0x50545448   /* "HTTP" */

// ngx_conf_file.h
#define NGX_CORE_MODULE      0x45524F43  /* "CORE" */
#define NGX_CONF_MODULE      0x464E4F43  /* "CONF" */

1.3 NGX_MODULE_V1_PADDING

這個還是用來填充欄位的,或者叫 padding、補白。

#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0

2 命令定義

命令定義用到如下資料結構:

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

一般類說會定義很多命令,但是在 HelloWorld 中只有一個命令。

static ngx_command_t ngx_http_hello_world_commands[] = {
    {
        ngx_string("hello_world"), // The command name
        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
        ngx_http_hello_world, // The command handler
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_hello_world_loc_conf_t, output_words),
        NULL
    },
    ngx_null_command
};

我們一個一個來看。

2.1 命令名稱

name 成員表示命令名稱

2.2 命令型別

type 是命令型別。它可以取如下的一個或多個值的“或”:

  • NGX_HTTP_MAIN_CONF:可出現在 http 的主作用域;
  • NGX_HTTP_SRV_CONF:可出現在 http 的 server 作用域;
  • NGX_HTTP_LOC_CONF:可出現在 http 的 location 作用域;
  • NGX_HTTP_UPS_CONF:可出現在 http 的 upstream 作用域;
  • NGX_HTTP_SIF_CONF:which will allow the directive to be included in if statements at the server level. [參考]
  • NGX_CONF_NOARGS:指令沒有引數;
  • NGX_CONF_TAKE1:指令讀入1個引數;
  • NGX_CONF_TAKE2:指令讀入2個引數;
  • NGX_CONF_TAKE7:指令讀入7個引數;
  • NGX_CONF_FLAG:指令讀入1個布林型資料(“on”或“off”);
  • NGX_CONF_1MORE:指令至少讀入1個引數;
  • NGX_CONF_2MORE:指令至少讀入2個引數;

      // ngx_http_config.h
      #define NGX_HTTP_MAIN_CONF        0x02000000
      #define NGX_HTTP_SRV_CONF         0x04000000
      #define NGX_HTTP_LOC_CONF         0x08000000
      #define NGX_HTTP_UPS_CONF         0x10000000
      #define NGX_HTTP_SIF_CONF         0x20000000
      #define NGX_HTTP_LIF_CONF         0x40000000
      #define NGX_HTTP_LMT_CONF         0x80000000
    
      // ngx_conf_file.h
      #define NGX_CONF_NOARGS      0x00000001
      #define NGX_CONF_TAKE1       0x00000002
      #define NGX_CONF_TAKE2       0x00000004
      #define NGX_CONF_TAKE3       0x00000008
      #define NGX_CONF_TAKE4       0x00000010
      #define NGX_CONF_TAKE5       0x00000020
      #define NGX_CONF_TAKE6       0x00000040
      #define NGX_CONF_TAKE7       0x00000080
    
      // ngx_conf_file.h      
      #define NGX_CONF_TAKE12      (NGX_CONF_TAKE1|NGX_CONF_TAKE2)
      #define NGX_CONF_TAKE13      (NGX_CONF_TAKE1|NGX_CONF_TAKE3)
      #define NGX_CONF_TAKE23      (NGX_CONF_TAKE2|NGX_CONF_TAKE3)
      #define NGX_CONF_TAKE123     (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3)
      #define NGX_CONF_TAKE1234    (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3|NGX_CONF_TAKE4)
    
      // ngx_conf_file.h
      #define NGX_CONF_ARGS_NUMBER 0x000000ff
      #define NGX_CONF_BLOCK       0x00000100
      #define NGX_CONF_FLAG        0x00000200
      #define NGX_CONF_ANY         0x00000400
      #define NGX_CONF_1MORE       0x00000800
      #define NGX_CONF_2MORE       0x00001000
      #define NGX_CONF_MULTI       0x00002000
    

2.3 命令回撥函式

這裡我們使用的是自己定義的回撥函式,還可以使用 Nginx 提供的回撥函式,比如:

  • ngx_conf_set_flag_slot:將“on”或者“off”轉換成1或0;
  • ngx_conf_set_str_slot:將字串儲存為ngx_str_t;
  • ngx_conf_set_num_slot:解析一個數字並儲存為ngx_int_t;
  • ngx_conf_set_size_slot:解析一個資料大小(如:“8k”,“1m”),並儲存為size_t;
  • ngx_conf_set_enum_slot:根據列舉定義將字串翻譯成ngx_int_t;
  • ngx_http_set_complex_value_slot:解析一個包含nginx變數的字串並儲存為ngx_http_complex_value_t;

2.4 儲存位置

conf 引數它有三個可能的取值,分別如下:

NGX_HTTP_MAIN_CONF_OFFSET
NGX_HTTP_SRV_CONF_OFFSET
NGX_HTTP_LOC_CONF_OFFSET

這可不是隨意指定的,如果你的type引數設定了NGX_HTTP_MAIN_CONF,那麼這裡就要設定為NGX_HTTP_MAIN_CONF_OFFSET。相應的,如果是NGX_HTTP_SRV_CONFNGX_HTTP_LOC_CONF,那麼這裡就要設定為NGX_HTTP_SRV_CONF_OFFSETNGX_HTTP_LOC_CONF_OFFSET

2.5 offset

表示資料具體儲存在main_conf、srv_conf、loc_conf指向的結構體的哪個位置(offset偏移)。大家可能會問,這個main_conf等等怎麼來的,nginx給我們挖的坑長得是個什麼樣子,這個我們在介紹ngx_http_hello_world_module_ctx會說到。

2.6 post

一個補充欄位,一般不用的,填入NULL。只是對於某些特殊的處理函式,比如ngx_conf_set_enum_slot,會用這個指標來指向enum定義表。

最後不要忘了加上 ngx_null_command,以表示命令集合定義完成。

3 上下文定義

用到了 HTTP 模組,定義如下:

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;

可看到有以下幾個部分:

  • Pre configuration
  • Post configuration
  • create main configuration
  • init main configuration
  • create server configuration
  • merge server configuration
  • create location configuration
  • merge location configuration

它們都是函式指標,都可以為 NULL,不過我們的模組裡用到的是:

// Structure for the HelloWorld context
static ngx_http_module_t ngx_http_hello_world_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_hello_world_create_loc_conf,
    ngx_http_hello_world_merge_loc_conf
};

這兩個函式會在下一篇文章中介紹。

4 Reference

  1. http://forum.nginx.org/read.php?2,243
  2. http://blog.sina.com.cn/s/blog_7303a1dc0100x70t.html

-

轉載請註明來自柳大的CSDN部落格:Blog.CSDN.net/Poechant

-

相關文章