Nginx Http框架的理解
HTTP框架是Nginx基礎框架的一部分,Nginx的其它底層框架如master-worker進程模型、event模塊、mail 模塊等。
HTTP框架代碼主要有2個模塊組成:ngx_http_module和ngx_http_core_module;
我們編寫的HTTP模塊需要註冊到HTTP框架上,才能融入HTTP請求的處理流程中。
當在nginx.conf中存在一個http{...}的配置時,即啟用了HTTP框架代碼,在nginx配置解析時,就已經為框架建立好了各種數據結構(尤其是HTTP模塊的掛載);
當nginx收到請求時,請求完全按照HTTP框架建立好的這種邏輯進行處理。
一、HTTP模塊開發基礎
開發一個HTTP模塊,需要下面幾個數據結構:
1. HTTP模塊配置結構
用於存儲從配置文件讀進來的相關指令參數; 配置模塊的context有三種,分別是main、server和location,它們分別位於於http{...}、server{...}和location{...}上下文中。 其名稱約定如下:ngx_http_<module name>_(main|srv|loc)_conf_t 2.HTTP 模塊配置指令 模塊的指令是定義在一個叫做ngx_command_t的靜態數組中的; ngx_command_t數組以ngx_null_command為終結符。 struct ngx_command_t {
ngx_str_t name; // 指令名稱
ngx_uint_t type; // 指令所在的context和包含的參數個數
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); // 解析配置,並將參數存入模塊配置結構體中
ngx_uint_t conf; // 指令參數的存儲位置
ngx_uint_t offset; // 指令參數的存儲偏移量
void *post;
}; 其中, type
b. 指向結構體 ngx_command_t 的指針
c. 指向模塊自定義配置結構體的指針 Nginx內部提供了多個函數用來保存特定類型的數據,這些函數包括: * ngx_conf_set_flag_slot: 將 "on" or "off" 轉換成 1 or 0 * ngx_conf_set_str_slot: 將字符串保存為 ngx_str_t * ngx_conf_set_num_slot: 解析一個數字並保存為int * ngx_conf_set_size_slot: 解析一個數據大小(如:"8k", "1m") 並保存為size_t conf 成員告訴Nginx把數據存在模塊的哪個context中 * NGX_HTTP_MAIN_CONF_OFFSET * NGX_HTTP_SRV_CONF_OFFSET * NGX_HTTP_LOC_CONF_OFFSET offset 成員確定保存在結構體的哪個位置; post 成員指向模塊在讀配置的時候需要的一些零碎變量,一般為NULL。 3. HTTP模塊上下文結構
靜態的ngx_http_module_t結構體,用來創建和合並三段context (main,server,location), 其命名方式一般是:ngx_http_<module name>_module_ctx, typedef struct {
ngx_int_t (*preconfiguration)(ngx_conf_t *cf); // 在讀入配置前調用
ngx_int_t (*postconfiguration)(ngx_conf_t *cf); // 在讀入配置後調用,用於掛載handler
void *(*create_main_conf)(ngx_conf_t *cf); // 在創建main配置時調用(比如,用來分配空間和設置默認值)
char *(*init_main_conf)(ngx_conf_t *cf, void *conf); // 在初始化main配置時調用(比如,把原來的默認值用nginx.conf讀到的值來覆蓋)
void *(*create_srv_conf)(ngx_conf_t *cf); // 在創建server配置時調用
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); // 合並server和main配置時調用
void *(*create_loc_conf)(ngx_conf_t *cf); // 創建location配置時調用,用於為指令參數結構體分配內存和初始化
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); // 合並location和server配置時調用
} ngx_http_module_t;
這些回調是在ngx_http_block()解析http{...}配置時完成的:
當遇到一個 http{...} 時,HTTP框架會調用所有HTTP模塊可能實現的create_main_conf、create_srv_conf、create_loc_conf生成存儲main級別配置參數結構體;
當遇到一個server{...}時,HTTP框架會調用所有HTTP模塊可能實現的create_srv_conf、create_loc_conf生成存儲server級別配置參數結構體;
當遇到一個location{...}時,HTTP框架會調用所有HTTP模塊可能實現的create_loc_conf生成存儲location級別配置參數結構體;
因此,我們開發的HTTP模塊中create_loc_conf方法被調用的次數等於http{...}、server{...}、location{...}在nginx.conf出現的次數之和;
create_srv_conf方法被調用的次數等於server{...}、location{...}在nginx.conf出現的次數之和;
由於只有一個http{...},所以create_main_conf方法只會被調用一次;
HTTP創建了如此多的結構體來存放配置項,是為了解決同名配置項的合並問題。
4、HTTP模塊定義 一個Nginx模塊被定義為一個ngx_module_t 結構, 該結構體變量命名方式為ngx_http_<module-name>_module 它包含模塊的內容和指令執行方式,同時還包含一些回調函數來處理線程/進程的創建和銷毀; 模塊定義在有的時候會被用作查找的關鍵字,來查找與特定模塊相關聯的數據。
struct ngx_module_s {
ngx_uint_t ctx_index; // 在所有的HTTP模塊中的序列號
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; // 模塊類型,HTTP模塊應為NGX_HTTP_MODULE
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;
};
註意:在configure之後生成的文件 objs/ngx_modules.c 中包含了模塊的編譯順序。
1、解析HTTP配置的流程
首先要理解 ngx_conf_parse() 的遞歸解析流程;
nginx在解析nginx.conf的時候,沒讀取一行配置項,就執行該配置項的解析回調(handler);
Nginx Http框架的理解