Nginx請求處理流程你瞭解嗎?
本文主要介紹了nginx的11個處理階段和lua的8個處理階段,並說明了
nginx和lua執行階段的對應關係。
上篇文章回顧:Linux網路程式設計之IO模型
nginx實際把http請求處理流程劃分為了11個階段,這樣劃分的原因是將請求的執行邏輯細分,以模組為單位進行處理,各個階段可以包含任意多個HTTP模組並以流水線的方式處理請求。這樣做的好處是使處理過程更加靈活、降低耦合度。這11個HTTP階段如下所示:
1)NGX_HTTP_POST_READ_PHASE:
接收到完整的HTTP頭部後處理的階段,它位於uri重寫之前,實際上很少有模組會註冊在該階段,預設的情況下,該階段被跳過。
2)NGX_HTTP_SERVER_REWRITE_PHASE:
URI與location匹配前,修改URI的階段,用於重定向,也就是該階段執行處於server塊內,location塊外的重寫指令,在讀取請求頭的過程中nginx會根據host及埠找到對應的虛擬主機配置。
3)NGX_HTTP_FIND_CONFIG_PHASE:
根據URI尋找匹配的location塊配置項階段,該階段使用重寫之後的uri來查詢對應的location,值得注意的是該階段可能會被執行多次,因為也可能有location級別的重寫指令。
4)NGX_HTTP_REWRITE_PHASE:
上一階段找到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)
三 nginx和lua執行階段的對應關係
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
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559359/viewspace-2221424/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- nginx 如何處理一個請求Nginx
- 圖解 Spring:HTTP 請求的處理流程與機制【1】圖解SpringHTTP
- 處理 HTTP 請求的註解HTTP
- SpringMVC底層——請求引數處理流程描述SpringMVC
- Tomcat 第四篇:請求處理流程(上)Tomcat
- Spring MVC 處理一個請求的流程分析SpringMVC
- Spring MVC框架處理Web請求的基本流程SpringMVC框架Web
- Tomcat總體架構,啟動流程與處理請求流程Tomcat架構
- nginx 處理客戶端請求的完整過程Nginx客戶端
- 理解 Nginx 在處理請求時的匹配規則Nginx
- 【原始碼分析】- 在SpringBoot中你會使用REST風格處理請求嗎?原始碼Spring BootREST
- Angular Universal Application 應該處理 HTTP POST 請求嗎?AngularAPPHTTP
- Scrapy原始碼閱讀分析_4_請求處理流程原始碼
- springmvc處理ajax請求SpringMVC
- 請求資料處理
- 你以為你請求的就是你想請求的嗎?
- Nginx處理請求的11個階段(agentzh的Nginx 教程學習記錄)Nginx
- 【Tomcat】Tomat 處理請求的過程(圖解)Tomcat圖解
- Spring MVC檔案請求處理詳解:MultipartResolverSpringMVC
- DeferredResult——非同步請求處理非同步
- CesiumJS 2022^ 原始碼解讀[7] - 3DTiles 的請求、載入處理流程解析JS原始碼3D
- Spring MVC原始碼(二) ----- DispatcherServlet 請求處理流程 面試必問SpringMVC原始碼Servlet面試
- 維護你的請求佇列,處理token異常佇列
- yai 請求預處理指令碼AI指令碼
- Netty(二):如何處理io請求?Netty
- Laravel請求處理管道理解Laravel
- springmvc原始碼 ---DispatcherServlet 處理請求SpringMVC原始碼Servlet
- Go Web如何處理Web請求?GoWeb
- 詳解nginx的請求限制(連線限制和請求限制)Nginx
- 請求基本流程
- java webservice 帶請求頭方式處理JavaWeb
- SpringBoot可以同時處理多少請求?Spring Boot
- Spring MVC的請求處理邏輯SpringMVC
- 使用cors完成跨域請求處理CORS跨域
- linux如何處理多連線請求?Linux
- SpringBoot 教程之處理非同步請求Spring Boot非同步
- 搜尋 伺服器處理請求伺服器
- options 請求跨域問題處理跨域