本文主要介紹了nginx的11個處理階段和lua的8個處理階段,並說明了nginx和lua運行階段的對應關係。
上篇文章回顧: Linux網絡編程之IO模型
1)NGX_HTTP_POST_READ_PHASE:nginx
接收到完整的HTTP頭部後處理的階段,它位於uri重寫以前,實際上不多有模塊會註冊在該階段,默認的狀況下,該階段被跳過。編程
2)NGX_HTTP_SERVER_REWRITE_PHASE:數組
URI與location匹配前,修改URI的階段,用於重定向,也就是該階段執行處於server塊內,location塊外的重寫指令,在讀取請求頭的過程當中nginx會根據host及端口找到對應的虛擬主機配置。緩存
3)NGX_HTTP_FIND_CONFIG_PHASE:bash
根據URI尋找匹配的location塊配置項階段,該階段使用重寫以後的uri來查找對應的location,值得注意的是該階段可能會被執行屢次,由於也可能有location級別的重寫指令。服務器
4)NGX_HTTP_REWRITE_PHASE:cookie
上一階段找到location塊後再修改URI,location級別的uri重寫階段,該階段執行location基本的重寫指令,也可能會被執行屢次。網絡
5)NGX_HTTP_POST_REWRITE_PHASE:數據結構
防止重寫URL後致使的死循環,location級別重寫的後一階段,用來檢查上階段是否有uri重寫,並根據結果跳轉到合適的階段。併發
6)NGX_HTTP_PREACCESS_PHASE:
下一階段以前的準備,訪問權限控制的前一階段,該階段在權限控制階段以前,通常也用於訪問控制,好比限制訪問頻率,連接數等。
7)NGX_HTTP_ACCESS_PHASE:
讓HTTP模塊判斷是否容許這個請求進入Nginx服務器,訪問權限控制階段,好比基於ip黑白名單的權限控制,基於用戶名密碼的權限控制等。
8)NGX_HTTP_POST_ACCESS_PHASE:
訪問權限控制的後一階段,該階段根據權限控制階段的執行結果進行相應處理,向用戶發送拒絕服務的錯誤碼,用來響應上一階段的拒絕。
9)NGX_HTTP_TRY_FILES_PHASE:
爲訪問靜態文件資源而設置,try_files指令的處理階段,若是沒有配置try_files指令,則該階段被跳過。
10)NGX_HTTP_CONTENT_PHASE:
處理HTTP請求內容的階段,大部分HTTP模塊介入這個階段,內容生成階段,該階段產生響應,併發送到客戶端。
11)NGX_HTTP_LOG_PHASE:
處理完請求後的日誌記錄階段,該階段記錄訪問日誌。
以上11個階段中,HTTP沒法介入的階段有4個:
3)NGX_HTTP_FIND_CONFIG_PHASE
5)NGX_HTTP_POST_REWRITE_PHASE
8)NGX_HTTP_POST_ACCESS_PHASE
9)NGX_HTTP_TRY_FILES_PHASE
剩餘的7個階段,HTTP模塊均能介入,每一個階段可介入模塊的個數也是沒有限制的,多個HTTP模塊可同時介入同一階段並做用於同一請求。
typedef structngx_http_phase_handler_s ngx_http_phase_handler_t;/*一個HTTP處理階段中的checker檢查方法,僅能夠由HTTP框架實現,以此控制HTTP請求的處理流程*/typedef ngx_int_t(*ngx_http_phase_handler_pt)(ngx_http_request_t *r, ngx_http_phase_handler_t*ph);/*由HTTP模塊實現的handler處理方法*/typedef ngx_int_t(*ngx_http_handler_pt)(ngx_http_request_t *r);
struct ngx_http_phase_handler_s { /*在處理到某一個HTTP階段時,HTTP框架將會在checker方法已實現的前提下首先調用checker方法來處理請求,
而不會直接調用任何階段中的hanlder方法,只有在checker方法中才會去調用handler方法,所以,事實上全部
的checker方法都是由框架中的ngx_http_core_module模塊實現的,且普通模塊沒法重定義checker方法*/
ngx_http_phase_handler_pt checker; /*除ngx_http_core_module模塊之外的HTTP模塊,只能經過定義handler方法才能介入某一個HTTP處理階段以處理請求*/
ngx_http_handler_pt handler; /*將要處理的下一個HTTP處理階段的序號
next的設計使得處理階段沒必要按順序依次執行,既能夠向後跳躍數個階段繼續執行,也能夠跳躍到以前的某個階段從新
執行,一般,next表示下一個處理階段中的第1個ngx_http_phase_handler_t處理方法*/
ngx_uint_t next;
};複製代碼
一個http{}塊解析完畢後,將會根據nginx.conf中的配置產生由ngx_http_phase_handler_t組成的數組,在處理HTTP請求時,通常狀況下這些階段是順序向後執行的,但ngx_http_phase_handler_t中的next成員使得它們也能夠非順序地執行,ngx_http_phase_engine_t結構體就是全部ngx_http_phase_handler_t組成的數組,以下所示:
typedef struct { /*handlers是由ngx_http_phase_handler_t構成的數組首地址,它表示一個請求可能經歷的全部ngx_http_handler_pt處理方法*/
ngx_http_phase_handler_t *handlers; /*表示NGX_HTTP_SERVER_REWRITE_PHASE階段第1個ngx_http_phase_handler_t處理方法在handlers數組中的序號,用於在執行
HTTP請求的任何階段中快速跳轉到HTTP_SERVER_REWRITE_PHASE階段處理請求*/
ngx_uint_t server_rewrite_index; /*表示NGX_HTTP_PREACCESS_PHASE階段第1個ngx_http_phase_handler_t處理方法在handlers數組中的序號,用於在執行
HTTP請求的任何階段中快速跳轉到NGX_HTTP_PREACCESS_PHASE階段處理請求*/
ngx_uint_t location_rewrite_index;
} ngx_http_phase_engine_t;複製代碼
能夠看到,ngx_http_phase_engine_t中保存了在當前nginx.conf配置下,一個用戶請求可能經歷的全部ngx_http_handler_pt處理方法,這是全部HTTP模塊能夠合做處理用戶請求的關鍵,這個ngx_http_phase_engine_t結構體保存在全局的ngx_http_core_main_conf_t結構體中,以下:
typedef struct {
ngx_array_t servers; /* ngx_http_core_srv_conf_t */
/*由下面各階段處理方法構成的phases數組構建的階段引擎纔是流水式處理HTTP請求的實際數據結構*/
ngx_http_phase_engine_t phase_engine;
ngx_hash_t headers_in_hash;
ngx_hash_t variables_hash;
ngx_array_t variables; /* ngx_http_variable_t */
ngx_uint_t ncaptures;
ngx_uint_t server_names_hash_max_size;
ngx_uint_t server_names_hash_bucket_size;
ngx_uint_t variables_hash_max_size;
ngx_uint_t variables_hash_bucket_size;
ngx_hash_keys_arrays_t *variables_keys;
ngx_array_t *ports;
ngx_uint_t try_files; /* unsigned try_files:1 */
/*用於在HTTP框架初始化時幫助各個HTTP模塊在任意階段中添加HTTP處理方法,它是一個有11個成員的ngx_http_phase_t數組,
其中每個ngx_http_phase_t結構體對應一個HTTP階段,在HTTP框架初始化完畢後,運行過程當中的phases數組是無用的*/
ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;複製代碼
在ngx_http_phase_t中關於HTTP階段有兩個成員:phase_engine和phases,其中phase_engine控制運行過程當中的一個HTTP請求所要通過的HTTP處理階段,它將配合ngx_http_request_t結構體中的phase_handler成員使用(phase_handler制定了當前請求應當執行哪個HTTP階段);而phases數組更像一個臨時變量,它實際上僅會在Nginx啓動過程當中用到,它的惟一使命是按照11個階段的機率初始化phase_engine中的handlers數組。
typedef struct { /*handlers動態數組保存着每個HTTP模塊初始化時添加到當前階段的處理方法*/
ngx_array_t handlers;
} ngx_http_phase_t;複製代碼
在HTTP框架的初始化過程當中,任何HTTP模塊均可以在ngx_http_module_t接口的postconfiguration方法中將自定義的方法添加到handler動態數組中,這樣,這個方法就會最終添加到phase_engine動態數組中。
init_by_lua http
set_by_lua server, server if, location, location if
rewrite_by_lua http, server, location, location if
access_by_lua http, server, location, location if
content_by_lua location, location if
header_filter_by_lua http, server, location, location if
body_filter_by_lua http, server, location, location if
log_by_lua http, server, location, location if
1)init_by_lua:
在nginx從新加載配置文件時,運行裏面lua腳本,經常使用於全局變量的申請。(例如:lua_shared_dict共享內存的申請,只有當nginx重起後,共享內存數據才清空,這經常使用於統計。)
2)set_by_lua:
流程分支處理判斷變量初始化(設置一個變量,經常使用與計算一個邏輯,而後返回結果,該階段不能運行Output API、Control API、Subrequest API、Cosocket API)
3)rewrite_by_lua:
轉發、重定向、緩存等功能 (例如特定請求代理到外網,在access階段前運行,主要用於rewrite)
4)access_by_lua:
IP准入、接口權限等狀況集中處理(例如配合iptable完成簡單防火牆,主要用於訪問控制,能收集到大部分變量,相似status須要在log階段纔有。這條指令運行於nginx access階段的末尾,所以老是在 allow 和 deny 這樣的指令以後運行,雖然它們同屬 access 階段。)
5)content_by_lua:
內容生成,階段是全部請求處理階段中最爲重要的一個,運行在這個階段的配置指令通常都肩負着生成內容(content)並輸出HTTP響應。
6)header_filter_by_lua:
應答HTTP過濾處理,通常只用於設置Cookie和Headers等,該階段不能運行Output API、Control API、Subrequest API、Cosocket API(例如添加頭部信息)。
7)body_filter_by_lua:
應答BODY過濾處理(例如完成應答內容統一成大寫)(通常會在一次請求中被調用屢次, 由於這是實現基於 HTTP 1.1 chunked 編碼的所謂「流式輸出」的,該階段不能運行Output API、Control API、Subrequest API、Cosocket API)
8)log_by_lua:
會話完成後本地異步完成日誌記錄(日誌能夠記錄在本地,還能夠同步到其餘機器)(該階段老是運行在請求結束的時候,用於請求的後續操做,如在共享內存中進行統計數據,若是要高精確的數據統計,應該使用body_filter_by_lua,該階段不能運行Output API、Control API、Subrequest API、Cosocket API)
1)init_by_lua,運行在initialization Phase;
2)set_by_lua,運行在rewrite 階段;
set 指令來自 ngx_rewrite 模塊,運行於 rewrite 階段;
3)rewrite_by_lua 指令來自 ngx_lua 模塊,運行於 rewrite 階段的末尾
4)access_by_lua 指令一樣來自 ngx_lua 模塊,運行於 access 階段的末尾;
deny 指令來自 ngx_access 模塊,運行於 access 階段;
5)content_by_lua 指令來自 ngx_lua 模塊,運行於 content 階段;不要將它和其它的內容處理指令在同一個location內使用如proxy_pass;
echo 指令則來自 ngx_echo 模塊,運行在 content 階段;
6)header_filter_by_lua 運行於 content 階段,output-header-filter 通常用來設置cookie和headers;
7)body_filter_by_lua,運行於 content 階段;
8)log_by_lua,運行在Log Phase 階段;
如圖:
參考資料:
1.https://blog.csdn.net/fb408487792/article/details/53610140
2.https://blog.csdn.net/lijinqi1987/article/details/53010000?locationNum=15&fps=1
3.https://blog.csdn.net/yangguanghaozi/article/details/54139258
本文首發於公衆號「小米運維」,點擊查看原文