原創:https://blog.csdn.net/ndzjx/article/details/89222809
HTTP框架大致由1個核心模塊(ngx_http_module)、兩個HTTP模塊(ngx_http_core_module、ngx_http_upstream_module)組成,它將負責調度其他HTTP模塊來一起處理用戶請求。首要任務是通過ngx_http_module_t接口中的方法來管理所有HTTP模塊的配置項。
Server虛擬主機會以散列表的數據結構組織起來,高效查詢。
Location表達式會以一個靜態的二叉查找樹組織起來。不使用紅黑樹,是因爲location是從nginx.conf中讀取到的,是靜態不變的,不存在運行過程中添加刪除的場景,而且紅黑樹的查詢效率也沒有重新構造的靜態的完全平衡二叉樹高。(ngx_http_init_locations/nginx_http_init_static_location_trees)
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;
1:處理http{}塊內的main級別配置項時,對每個HTTP模塊,都會調用create_main_conf、create_srv_conf、create_loc_conf建立3個結構體,這是爲了把同時出現在http{},server{},location{}內的相同配置項進行合併而做的準備。
2:處理server{}內的srv級別配置項時,需要調用每個HTTP模塊的create_srv_conf、create_loc_conf
3:處理location{}內的loc級別配置項時,調用HTTP每個模塊的create_loc_conf方法建立結構體
解析http{}
ngx_http_core_module模塊完成了HTTP框架的大部分功能,而它又是一個HTTP模塊,使用到了比較關心的3個結構體(ngx_http_core_main_conf_t、ngx_http_core_srv_conf_t、ngx_http_core_loc_conf_t)
typedef struct {
void **main_conf;
void **srv_conf;
void **loc_conf;
} ngx_http_conf_ctx_t;
通過ngx_cycle_t找到main級別的配置結構體:
#define ngx_http_cycle_get_module_main_conf(cycle, module) \
(cycle->conf_ctx[ngx_http_module.index] ? \
((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]) \
->main_conf[module.ctx_index]: \
NULL)
解析server塊
首先會像解析http塊一樣,建立屬於這個server塊的ngx_http_conf_ctx_t結構體。
如果server{}內沒有解析到listen配置項,默認監聽端口80,當進程沒有權限監聽1024以下的端口時,則會監聽8000端口。
ngx_http_core_module模塊的ngx_http_core_main_conf_t結構中有一個servers動態數組。
解析location配置塊
仍然會像解析http塊一樣,先建立ngx_http_conf_ctx_t結構體。
同一個server下的location, ngx_http_core_loc_conf_t結構體構成一個雙向鏈表。(非侵入式鏈表)
location下還可以嵌套location,同樣,會生成鏈表。
監聽端口的管理:
監聽端口屬於server虛擬主機,由listen配置項決定,與server{}塊對應的ngx_http_core_srv_conf_t結構體密切相關。每一個TCP端口,都將使用一個獨立的ngx_http_conf_port_t結構體表示。
ngx_http_conf_port_t的addrs動態數組可能不太容易理解。如:對同一個端口8000,我們可以同時監聽127.0.0.1:8000、173.39.160.51:8000這兩個地址,當一臺物理機器具備多個IP地址時這是很有用的
監聽端口與server虛擬主機的關係:
HTTP請求的11個階段:
1)post_read : 接受到完整的http頭部後處理的階段
2)server_rewrite:URI與location匹配前,可以修改URI(重定向)
3)find_config:根據URI尋找匹配的location表達式
4)rewrite:location匹配後可以再修改URI
5)post_rewrite:rewrite修改URI後,防止錯誤的nginx.conf配置導致死循環,如果一個請求超過10次重定向,就認爲i進入了rewrite死循環。
6)pre_access:處理訪問權限前,可以介入的階段
7)access:是否允許請求訪問Nginx服務器
8)post_access:如果不允許訪問,向用戶發送拒絕服務的錯誤響應。
9)pre_content:包含try_files指令,訪問靜態文件資源
10)content:處理HTTP請求內容的階段
11)log:處理完請求後記錄日誌的階段
其中:find_config、post_rewrite、post_access、pre_content 4個階段不允許HTTP模塊加入自己的ngx_http_handler_pt方法處理用戶請求,它們僅由HTTP框架實現。
struct ngx_http_phase_handler_s {
ngx_http_phase_handler_pt checker; // 由ngx_http_core_module實現
ngx_http_handler_pt handler;
ngx_uint_t next; // 使得處理階段不必按順序依次執行
};
ngx_http_phase_engine_t結構體就是所有ngx_http_phase_handler_t組成的數組。此結構體在全局的ngx_http_core_main_conf_t結構體中。ngx_http_core_main_conf_t中還有一個phases數組,保存11個階段每個階段要執行的handlers
typedef struct {
ngx_http_phase_handler_t *handlers;
ngx_uint_t server_rewrite_index; // 跳轉到server_rewrite階段處理
ngx_uint_t location_rewrite_index; // 跳轉的rewrite
} ngx_http_phase_engine_t;
向指定階段添加handler的方法大致如下:
在ngx_http_module_t接口的postconfiguration方法中將自定義的方法添加到handler動態數組中。
h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_realip_handler;
其中,content階段還有一個獨有的方法可以添加handler:
把希望處理請求的ngx_http_handler_pt方法設置到location相關的ngx_http_core_loc_conf_t結構體的handler指針中。
好處是:
處理方法不再應用於所有HTTP請求,僅URI匹配時纔會被調用。
注意:
這種情況下,每個content階段只能有一個ngx_http_handler_pt處理方法。這種方式優先於使用postconfiguration設置的方法,postconfiguration設置的方法會失效。
ngx_http_block方法,包括了HTTP框架的完整初始化流程: