nginx源碼分析——模塊

1. 模塊介紹

高度模塊化的設計是nginx的架構基礎。在nginx中,除了少許的核心代碼,其餘一切皆爲模塊。這種模塊化設計同時具備如下幾個特色:nginx

  • 高度抽象的模塊接口

    全部的模塊都遵循着一樣的 ngx_module_t 接口設計規範,這減小了整個系統中的變數。數組

  • 模塊接口很是簡單,具備很高的靈活性

    模塊的基本接口 ngx_module_t 足夠簡單,只涉及模塊的初始化、退出以及對配置項的處理、這同時也帶來了足夠的靈活性,使得nginx比較簡單地實現了動態可修改性。多線程

  • 配置模塊的設計

    ngx_module_t接口有一個type成員,它指明瞭nginx容許在設計模塊時定義模塊類型這個概念,容許專一於不一樣領域的模塊按照類型來區別。而配置類型模塊是惟一一種只有1個模塊的模塊類型。配置模塊的類型叫作NGX_CONF_MODULE,它僅有的模塊叫作ngx_conf_module。這是nginx最底層的模塊,它指導着全部模塊以配置項爲核心來提供功能。所以,它是其餘全部模塊的基礎。配置模塊使nginx提供了高可配置性,高可擴展性,高可定製性,高可伸縮性。架構

  • 核心模塊接口的簡單化

    nginx中定義了一種基礎類型的模塊——核心模塊。核心模塊的存在簡化了nginx的設計,使得非模塊化的框架代碼只關注於如何調用這些核心模塊。框架

  • 多層次、多類別的模塊設計

    全部的模塊間是分層次的,分類別的。不一樣的模塊雖然都具有相同的ngx_module_t接口,單在請求處理流程中的層次並不相同。nginx將各功能組織成一條鏈,當有請求到達的時候,請求依次通過這條鏈上的部分或者所有模塊進行處理。模塊化

2. 模塊的分類

nginx模塊分爲核心模塊和功能性模塊,功能性模塊包括conf、event、http、mail、stream幾大類。除了conf類之外,其餘幾類功能性模塊都包含衆多的模塊,而且每一個類別中都有一個核心模塊。其主要做用是提供本類別功能通用的框架接口,邏輯處理等,並調用同類型的其餘模塊完成具體的功能。例如NGX_HTTP_MODULE類別中的核心模塊ngx_http_core_module,負責http塊配置項中通用的配置解析、http請求處理的總體流程,並調用其餘NGX_HTTP_MODULE模塊完成具體的處理。函數

nginx中經常使用模塊分層分類:post


3. 模塊的相關結構體

  • ngx_module_t接口
struct ngx_module_s {
    ngx_uint_t      ctx_index;                           // 模塊在同類型模塊數組中的索引序號
    ngx_uint_t      index;                               // 模塊在全部模塊數組中的索引序號
    char *          name;                                // 模塊的名稱
    ngx_uint_t      spare0;                              // 保留變量
    ngx_uint_t      spare1;                              // 保留變量
    ngx_uint_t      version;                             // 模塊的版本號 目前只有一種,默認爲1
    const char*     signature;
    void *          ctx;                                 // 模塊上下文結構體 不一樣的模塊指向不一樣的結構體
    ngx_command_t * commands;                            // 模塊關心的配置項表 配置文件解析時會查找該表
    ngx_uint_t      type;                                // 模塊類型 如NGX_CORE_MODULE
    ngx_int_t       (*init_master)(ngx_log_t *log);      // master進程啓動時回調
    ngx_int_t       (*init_module)(ngx_cycle *cycle);    // 初始化模塊時回調
    ngx_int_t       (*init_process)(ngx_cycle_t *cycle); // worker進程啓動時回調
    ngx_int_t       (*init_thread)(ngx_cycle_t *cycle);  // 線程啓動時回調(nginx暫時無多線程模式)
    void            (*exit_thread)(ngx_cycle_t *cycle);  // 線程退出時回調
    void            (*exit_process)(ngx_cycle_t *cycle); // worker進程退出時回調
    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;
};

typedef struct ngx_module_s  ngx_module_t;
  • 配置項
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;
    // 配置項讀取後的處理方法 必須是ngx_conf_post_t結構體的指針
    void *       post;
};

typedef struct ngx_command_s  ngx_command_t;
  • 核心模塊類型的上下文
typedef struct {
    // 模塊的名稱 例如 core, http, mail
    ngx_str_t    name;
    // 分配內存(建立相關結構體)用於存儲配置項解析時配置項值的存儲
    void *       (*create_conf)(ngx_cycle_t *cycle);
    // 初始化配置項的初始值
    char *       (*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t ;

4. 模塊初始化流程

  • 編譯生成ngx_modules.c

    執行configure後,會在objs目錄下生成nginx_modules.c源文件。這個源文件中有兩個很重要的全局變量ngx_modules和ngx_module_names,前者保存了nginx將要使用的所有模塊,後者則記錄了這些模塊的名稱。ui

ngx_module_t *ngx_modules[] = {
    &ngx_core_module,
    &ngx_errlog_module,
    &ngx_conf_module,
    &ngx_openssl_module,
    &ngx_regex_module,
    &ngx_events_module,
    &ngx_event_core_module,
    ...
};

char *ngx_module_names[] = {
    "ngx_core_module"
    "ngx_errlog_module",
    "ngx_conf_module",
    "ngx_openssl_module",
    "ngx_regex_module",
    "ngx_events_module",
    "ngx_event_core_module",
    ...
};
  • 預初始化

    初始化每一個模塊的索引以及名稱。spa

ngx_int_t  ngx_preinit_modules(void)
{
    ngx_uint_t  i;
    for( i = 0; ngx_modules[i]; i++ )
    {
        ngx_modules[i]->index = i;
        ngx_modules[i]->name = ngx_module_names[i];
    }

    ngx_modules_n = i;
    ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;

    return NGX_OK;
}
  • 針對核心模塊建立配置解析時須要的上下文
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
{
    ...
    for( i = 0; cycle->modules[i]; i++ ) {
        if( cycle->modules[i]->type != NGX_CORE_MODULE ) {
            continue;
        }
        module = cycle->modules[i]->ctx;
        if(module->create_conf) {
            rv = module->create_conf(cycle);
            if( rv == NULL ) {
                ngx_destroy_pool(pool);
                return NULL;
            }
            cycle->conf_ctx[cycle->modules[i]->index] = rv;
        }
    }
    ...
}
  • 查找模塊的配置項表並調用對應處理函數完成配置文件解析
  • 初始化核心模塊的配置
​ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
{
    ...
    for( i = 0; cycle->modules[i]; i++ ) {
        if( cycle->modules[i]->type != NGX_CORE_MODULE ) {
            continue;
        }
        module = cycle->modules[i]->ctx;
        if(module->init_conf) {
            if( module->init_conf(cycle, cycle->conf_ctx[cycle->modules[i]->index]) == NGX_CONF_ERROR ) {
                environ = senv;
                ngx_destroy_cycle_pools(&conf);
                return NULL;
            }
        }
    }
    ...
}
  • 初始化各個模塊
ngx_int_t ngx_init_modules(ngx_cycle_t * cycle)
{
    ngx_uint_t i;
    // 目前只有三個模塊該函數指針非空
    // 分別是 event_core, http_v2, regex
    for( i = 0; cycle->modules[i]; i++ ) {
        if(cycle->modules[i]->init_module) {
            if(cycle->modules[i]->init_module(cycle) != NGX_OK) {
                return NGX_ERROR;
            }
        }
    }
    return NGX_OK;
}
  • 調用模塊的進程啓動初始化回調函數
// init_master 回調函數當前沒有使用 即每一個模塊都設置爲NULL
// init_process 回調函數當前有event_core, http_userid_filter, thread_pool模塊進行設置
for( i = 0 ; cycle->modules[i]; i++ ) {
    if( cycle->modules[i]->init_process ) {
        if( cycle->modules[i]->init_process(cycle) == NGX_ERROR ) {
            exit(2);
        }
    }
}

到此模塊初始化流程結束,master/worker進程開始各自的循環。


參考:

《深刻理解nginx》

《nginx開發從入門到精通》

相關文章
相關標籤/搜索