其中,ngx_module_s,這 個是 每個新的模塊的定義
struct ngx_module_s {
/*ctx_index表示當前模塊在這類模塊中的序號。這個 成員常常是由管理模塊的 一個Nginx核心模塊設置的,對於所有的http模塊而言,ctx_index是由核心模塊ngx_http_module設置的,ctx_index非常重要,Nginx的模塊化設計非常依賴各個模塊的順序*/
ngx_uint_t ctx_index;
/*index表示當前模塊在ngx_modules數組中 的序號。*/
ngx_uint_t index;
char *name;
// sqare系列的 保留變量,暫未使用
ngx_uint_t spare0;
ngx_uint_t spare1;
// 模塊的版本
ngx_uint_t version;
const char *signature;
/**ctx用於指向一類的模塊的上下文結構體,Nginx模塊有許多種類,不同模塊之間的功能差別很大。例如事件模塊主要處理I/O事件相關的功能,每個模塊都有自己的特性,ctx將會指向特定類型模塊的公共接口,例如在Http 模塊 中,ctxz指向ngx_http_module_t結構體*/
void *ctx;
// commands將處理nginx.conf中 的配置項
ngx_command_t *commands;
/*type 表示該模塊的類型,它與ctx指針是緊密相關的,NGX_HTTP_MODULE、NGX_CORE_MODULE、NGX_CONF_MODULE、NGX_EVENT_MODULE、NGX_EVENT_MODULE、NGX_STREAM_MODULE*/
ngx_uint_t type;
/*在Nginx 啓動、停止過程中,以下7個函數指針表示有7個執行點分別調用這個7個方法,對於任一方法,如果不需要Nginx在某個時刻執行 它,那麼 簡單地把它設爲NULL空指針 即可*/
/* 雖然字面上理解應當在master進程啓動時調用,但到目前爲止,框架代碼從來不會調用它,因此 要設爲NULL*/
ngx_int_t (*init_master)(ngx_log_t *log);
/*init module 回調方法在初始化所有模塊被調用,在master/worker模式下,這個階段將在worker進程前完成 */
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
/*init_process回調方法在正常服務前被調用,在master/worker模式下,多個worker子進程已經產生,在每個worker進程初始化會調用所有模塊的init_process函數*/
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
// Nginx目前還支持多線程,所以這個沒有被調用過
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
/* exit_process回調方法在服務停止前調用。在master/worker模式下,worker會在退出前調用它*/
void (*exit_process)(ngx_cycle_t *cycle);
//該方法 會在master進程退出前調用它
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;
};
回調方法:init_module、init_process、exit_process、exit_master,調用它們是Nginx 的 框架代碼 。
所以定義模塊的 時候,最重要的就是要設置ctx和commands 這兩個成員。ctx指向的 是相應的接口(結構 體 )。框架會在讀取、重新配置文件定義了ctx指向的接口描述 的 幾個階段,框架會在啓動過程中調接口中定義的相應方法。如果相應的某個回調函數設爲NULL,那麼框架不會調用這個回調。
下面 我舉一個ctx指向的ngx_http_module_t結構體 (不同模塊指向不一樣)
typedef struct {
// 解析配置文件前函數調用
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
// 完成配置文件的解析後調用
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
/*當需要創建數據結構用於存儲main級別(直屬http{...}塊的配置項)的 全局配置 項時,可以通過這個回調方法 創建存儲全局配置項的結構體*/
void *(*create_main_conf)(ngx_conf_t *cf);
// 常用於初始化main級別配置項
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
/*當需要創建數據結構用於存儲srv級別(直屬於虛擬機server{}塊配置項),可以通過通過實現這個方法創建srv級別配置項的結構體*/
void *(*create_srv_conf)(ngx_conf_t *cf);
//合併main級別和srv級別下的同名配置
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
/*當需要創建數據結構用於存儲loc級別的(直屬於location{}塊 配置項)*/
void *(*create_loc_conf)(ngx_conf_t *cf);
// 合併serv級別和loc級別下的同名配置
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
說完 了ctx接口 ,接下來我們 進入另一個核心的結構commands
commands數組是用於定義模塊配置文件的參數,每一個數組元素都是ngx_command_t類型,數組的結尾用ngx__null_command表示,Nginx在解析 配置文件中一個 配置項時,首先會遍歷所有模塊,對於每一個模塊而言,即通過遍歷commands數組進行 ,在數組中檢查到 ngx_null_command,就會停止當前模塊解析該 配置項。
struct ngx_command_s {
// 配置項的名稱 比如 "http"
ngx_str_t name;
/*配置項類型,type將 指定配置項可以出現的位置,例如出現在server{}或location{}中,已經他可以攜帶參數的個數*/
ngx_uint_t type;
// 出現了name指定的配置項後,將會調用set方法處理該配置項的參數
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
//在配置項中的偏移量
ngx_uint_t conf;
//和conf參數配合使用
ngx_uint_t offset;
//配置項讀取配置的處理方法 ,必須是ngx_conf_post_t結構的指針
void *post;
};
ngx_null_command只是 一個空的ngx_command_t
#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }
下面是一個command定義的例子
static ngx_command_t ngx_http_circle_gif_commands[] = {
{ ngx_string("circle_gif"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_circle_gif,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("circle_gif_min_radius"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|
NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_circle_gif_loc_conf_t, min_radius),
NULL },
...
ngx_null_command
};
type選項
type參數 | 含義 |
---|---|
NGX_HTTP_MAIN_CON | 指令出現在全局配置部分是合法的 |
NGX_HTTP_SRV_CONF | 指令在主機配置部分出現是合法的 |
NGX_HTTP_LOC_CONF | 指令在位置配置部分出現是合法的 |
NGX_HTTP_UPS_CONF | 指令在上游服務器配置部分出現是合法的 |
NGX_CONF_NOARGS | 指令沒有參數 |
NGX_CONF_TAKE1 | 指令讀入一個參數 |
NGX_CONF_TAKE2 | 指令讀入兩個參數 |
….. | ….. |
NGX_CONF_TAKE7 | 指令讀入七個參數 |
NGX_CONF_FLAG | 指令讀入一個布爾型數據 |
NGX_CONF_1MORE | 指令至少讀入 1 個參數 |
NGX_CONF_2MORE | 指令至少讀入 2 個參數 |
總結
nginx 主進程在啓動時,就會在代碼裏面找相應的ngx_module_t(ngx_XXX_module)變量,找到以後,在其中ngx_command_t(ngx_xxx_commands)指定函數ngx
_xxx_init開始初始化模塊。所有的工作都要在這裏進行。
Nginx啓動時,會先啓動一個master管理進程,然後根據配置啓動數個worker進程。實際的module裏的勾子函數(例如ngx_XXX_handle),都是被worker進程所調用的。默認情況下,nginx並不是多線程的,所以,如果你的勾子函數被調用了,那麼你絕對不可以有任何阻塞操作,否則會使得nginx worker不去處理已經在鏈表中的其他connection,這就完全毀了nginx,如果你去同步請求硬盤IO資源,否則其他SERVER的網絡IO,那麼它和apach+CGI這種低性能SERVER也沒啥兩樣了,除了epoll可以hold住大量連接。