在了解nginx的執行階段前,先看一個例子
對echo不熟悉的,可以先看文章Nginx調試必備了解下echo擴展
?
回到上面這個例子,在server塊中配置這樣的location,你覺得輸出是什么樣子?
?
按照正常的邏輯,輸出應該是32 56,我們請求下,看下nginx處理的結果
?
兩次輸出都是56,顛覆認知。這就是因為set和echo處在nginx不同的執行階段,在nginx中,處在不同階段的配置,和配置文件順序沒有任何關系
?
Nginx處理請求過程總共劃分為11個階段,按順序依次是post-read、server-rewrite、find-config、rewrite、post-rewrite、preaccess、access、post-access、precontent、content以及log
?
看一下nginx源碼中的定義,在http/ngx_http_core_module.h中
我這里是1.17.7版本的nginx,我看了nginx1.16.0版本一樣,老一點的版本內容預處理階段是try_files階段
?
下面結合測試詳細說一下每個階段
?
post-read 階段
?
?
該階段是nginx接收完請求頭之后的第一個階段,它位于uri重寫之前,該階段很少用,很少有模塊會注冊在該階段,默認情況下,該階段被跳過,但是有個兩個標準函數是注冊在這個階段的,set_real_ip_from、real_ip_header
?
看到real_ip有沒有很熟悉,通常nginx日志或者后端需要獲取客戶端真實IP的時候,需要把cdn或者你前端的nginx或者代理的ip改寫之后來獲取,就會用到這個,下面看例子
?
?
通過curl -H添加header頭請求,然后查看結果
?
通常多級代理,后端要獲取真實IP就是通過這種自定義header的方式去獲取
?
?
server-rewrite 階段
?
?
該階段是server級別的uri重寫階段,該階段執行處于server塊內,location塊外的重寫指令,在讀取請求頭的過程中nginx會根據host及端口找到對應的虛擬主機配置
?
該階段不只是執行rewrite指令,通常該階段包含標準函數ngx_rewrite、set,看例子
?
?
這里在server塊內,location塊外set一個變量$a,在location中引用,因為server-rewrite階段在前面,所以$a變量會先賦值,查看結果
?
?
find-config 階段
?
?
該階段是尋找location配置階段,該階段使用重寫之后的uri來查找對應的location,如果匹配到的location中有重寫指令的話,該階段會再次執行,直到匹配到最終的location
?
這個階段的匹配工作是由nginx核心模塊來完成的,并不支持nginx模塊注冊處理程序
?
這個階段不太好整例子,想來想去沒有想到可以體現的例子,但是debug日志可以體現,用上一個階段的例子的請求日志看下
?
?
很直觀,在執行完上個階段的set $a之后,就開始匹配location,最終匹配到test,接著執行下面的階段
?
?
rewrite 階段
?
?
該階段是location級別的uri重寫階段,該階段執行location基本的重寫指令,同樣也可能被指定多次
?
直接寫個跳轉看日志
?
通過curl -L跟隨跳轉請求看下結果
?
查看debug日志
?
仍然是先執行server塊內的set,之后匹配到rewrite的location,然后執行location內的rewrite
?
?
post-rewrite階段
?
?
該階段是location級別重寫的后一個階段,用來檢查上階段是否有uri重寫,并根據結果跳轉到合適的階段
?
這個階段緊接上一個階段,是由nginx核心完成rewrite階段所要求的跳轉,即內部跳轉
?
內部跳轉本質上其實就是把當前的請求處理階段跳回到find-config階段,類似于條件分支循環,這也就是上面說到find-config階段會被多次執行的原因。把上階段的rewrite請求跳轉到find-config階段,重新進行請求uri和location配置塊的配對,這個過程可以從上面階段的后續debug日志可以看出來
?
rewrite被改寫到test,然后開始重新匹配uri為test的location
?
接著執行find-config階段的匹配過程,然后執行后面的階段
?
?
preaccess 階段
?
?
該階段是訪問權限控制的前一階段,預控制階段。該階段在權限控制階段之前,一般也用于訪問控制,比如limit的限制速率、限制連接數等
?
該階段包含的標準函數ngx_limit_req、ngx_limit_zone等
?
實例結合后面的下個階段一起看
?
?
access 階段
?
?
該階段是權限訪問控制階段,比如基于IP黑白名單的權限控制,基于用戶名密碼的權限控制等
?
該階段包含的標準函數ngx_access、ngx_auth_request函數等
?
結合preaccess,我們在同一個location中配置limit和access,如下
?
然后請求access看下結果
?
看下debug日志,分析處理階段
?
先處理limit,然后接著處理access部分
?
post-access 階段
?
?
該階段是訪問控制的后一階段,和post-rewrite階段類似,不支持nginx模塊注冊處理程序,由nginx核心自己完成處理工作,主要是配合access階段實現后續處理
?
這里常用的指令是satisfy,它的功能類似if判斷中的“與”、“或”關系,在access階段可以注冊多個nginx模塊,比如上面提到的access模塊和auth認證模塊,如果兩個模塊都注冊了,那么是執行哪個?按哪個匹配結果來執行,這個時候就用到satisfy,它就是讓多個控制之間彼此協作
?
比如上面兩個模塊都在access階段注冊了與訪問控制相關的處理程序,那就有兩種協作方式,一是模塊access和auth都通過驗證才算通過,另外一種是只要其中任一通過驗證就算通過。第一種方式為all方式,也就是“與”關系,第二種方式為any方式,也就是“或”關系,默認情況下nginx使用的是all方式
?
?
precontent 階段
?
?
該階段為生成內容的前一階段,主要是用于處理try_files指令的配置,如果沒有配置try_files指令,這個階段會跳過,該階段不支持nginx模塊注冊處理程序
?
try_files指令接受兩個以上任意數量的參數,每個參數都指定一個uri,比如設置N個參數,nginx會在precontent階段依次把前N-1個參數映射為文件系統上的文件或目錄,逐個檢查這些文件或目錄是否存在。一旦Nginx匹配到某個文件或目錄存在,就會在precontent階段,把當前請求的uri改寫為該對象所對應的參數uri,如果前N-1個參數所對應的文件或目錄都不存在,則precontent階段會發起內部跳轉,按照最后一個參數所指定的uri進行find-config階段
?
配置個try_files來看下執行過程
?
請求try,看下結果
并沒有執行echo uri部分,而是到test的location了,看下debug日志
?
可以看到和我們上面說的結果一致
?
?
content 階段
?
?
該階段是所有階段中最重要的一個階段,該階段負責內容生成,并輸出http響應。通過nginx配置文件中的配置指令,生成響應內容,返回給客戶端,這個階段的配置指令例如echo、proxy_pass等
?
日志體現該階段
?
?
?
log 階段
?
?
?該階段就是日志記錄階段,根據log配置寫入日志文件,比如log級別,日志格式logformat等,包含nginx的access_log和error_log等
?
在debug日志中也有記錄
請求返回給客戶端后,記錄日志,然后保持keepalive,如果是不需要keepalive的時候,直接close連接
?
以上就是nginx處理請求的11個階段,熟悉之后,對nginx的了解更深