Nginx 的請(qǐng)求處理

2022-03-23 15:39 更新

Nginx 使用一個(gè)多進(jìn)程模型來(lái)對(duì)外提供服務(wù),其中一個(gè) master 進(jìn)程,多個(gè) worker 進(jìn)程。master 進(jìn)程負(fù)責(zé)管理 Nginx 本身和其他 worker 進(jìn)程。

所有實(shí)際上的業(yè)務(wù)處理邏輯都在 worker 進(jìn)程。worker 進(jìn)程中有一個(gè)函數(shù),執(zhí)行無(wú)限循環(huán),不斷處理收到的來(lái)自客戶(hù)端的請(qǐng)求,并進(jìn)行處理,直到整個(gè) Nginx 服務(wù)被停止。

worker 進(jìn)程中,ngx_worker_process_cycle()函數(shù)就是這個(gè)無(wú)限循環(huán)的處理函數(shù)。在這個(gè)函數(shù)中,一個(gè)請(qǐng)求的簡(jiǎn)單處理流程如下:

  • 操作系統(tǒng)提供的機(jī)制(例如 epoll, kqueue 等)產(chǎn)生相關(guān)的事件。
  • 接收和處理這些事件,如是接受到數(shù)據(jù),則產(chǎn)生更高層的 request 對(duì)象。
  • 處理 request 的 header 和 body。
  • 產(chǎn)生響應(yīng),并發(fā)送回客戶(hù)端。
  • 完成 request 的處理。
  • 重新初始化定時(shí)器及其他事件。

請(qǐng)求的處理流程

為了讓大家更好的了解 Nginx 中請(qǐng)求處理過(guò)程,我們以 HTTP Request 為例,來(lái)做一下詳細(xì)地說(shuō)明。

從 Nginx 的內(nèi)部來(lái)看,一個(gè) HTTP Request 的處理過(guò)程涉及到以下幾個(gè)階段。

  • 初始化 HTTP Request(讀取來(lái)自客戶(hù)端的數(shù)據(jù),生成 HTTP Request 對(duì)象,該對(duì)象含有該請(qǐng)求所有的信息)。
  • 處理請(qǐng)求頭。
  • 處理請(qǐng)求體。
  • 如果有的話,調(diào)用與此請(qǐng)求(URL 或者 Location)關(guān)聯(lián)的 handler。
  • 依次調(diào)用各 phase handler 進(jìn)行處理。

在這里,我們需要了解一下 phase handler 這個(gè)概念。phase 字面的意思,就是階段。所以 phase handlers 也就好理解了,就是包含若干個(gè)處理階段的一些 handler。

在每一個(gè)階段,包含有若干個(gè) handler,再處理到某個(gè)階段的時(shí)候,依次調(diào)用該階段的 handler 對(duì) HTTP Request 進(jìn)行處理。

通常情況下,一個(gè) phase handler 對(duì)這個(gè) request 進(jìn)行處理,并產(chǎn)生一些輸出。通常 phase handler 是與定義在配置文件中的某個(gè) location 相關(guān)聯(lián)的。

一個(gè) phase handler 通常執(zhí)行以下幾項(xiàng)任務(wù):

  • 獲取 location 配置。
  • 產(chǎn)生適當(dāng)?shù)捻憫?yīng)。
  • 發(fā)送 response header。
  • 發(fā)送 response body。

當(dāng) Nginx 讀取到一個(gè) HTTP Request 的 header 的時(shí)候,Nginx 首先查找與這個(gè)請(qǐng)求關(guān)聯(lián)的虛擬主機(jī)的配置。如果找到了這個(gè)虛擬主機(jī)的配置,那么通常情況下,這個(gè) HTTP Request 將會(huì)經(jīng)過(guò)以下幾個(gè)階段的處理(phase handlers):

  • NGX_HTTP_POST_READ_PHASE: 讀取請(qǐng)求內(nèi)容階段
  • NGX_HTTP_SERVER_REWRITE_PHASE: Server 請(qǐng)求地址重寫(xiě)階段
  • NGX_HTTP_FIND_CONFIG_PHASE: 配置查找階段:
  • NGX_HTTP_REWRITE_PHASE: Location請(qǐng)求地址重寫(xiě)階段
  • NGX_HTTP_POST_REWRITE_PHASE: 請(qǐng)求地址重寫(xiě)提交階段
  • NGX_HTTP_PREACCESS_PHASE: 訪問(wèn)權(quán)限檢查準(zhǔn)備階段
  • NGX_HTTP_ACCESS_PHASE: 訪問(wèn)權(quán)限檢查階段
  • NGX_HTTP_POST_ACCESS_PHASE: 訪問(wèn)權(quán)限檢查提交階段
  • NGX_HTTP_TRY_FILES_PHASE: 配置項(xiàng) try_files 處理階段
  • NGX_HTTP_CONTENT_PHASE: 內(nèi)容產(chǎn)生階段
  • NGX_HTTP_LOG_PHASE: 日志模塊處理階段

在內(nèi)容產(chǎn)生階段,為了給一個(gè) request 產(chǎn)生正確的響應(yīng),Nginx 必須把這個(gè) request 交給一個(gè)合適的 content handler 去處理。如果這個(gè) request 對(duì)應(yīng)的 location 在配置文件中被明確指定了一個(gè) content handler,那么Nginx 就可以通過(guò)對(duì) location 的匹配,直接找到這個(gè)對(duì)應(yīng)的 handler,并把這個(gè) request 交給這個(gè) content handler 去處理。這樣的配置指令包括像,perl,flv,proxy_pass,mp4等。

如果一個(gè) request 對(duì)應(yīng)的 location 并沒(méi)有直接有配置的 content handler,那么 Nginx 依次嘗試:

  • 如果一個(gè) location 里面有配置 random_index on,那么隨機(jī)選擇一個(gè)文件,發(fā)送給客戶(hù)端。
  • 如果一個(gè) location 里面有配置 index 指令,那么發(fā)送 index 指令指明的文件,給客戶(hù)端。
  • 如果一個(gè) location 里面有配置 autoindex on,那么就發(fā)送請(qǐng)求地址對(duì)應(yīng)的服務(wù)端路徑下的文件列表給客戶(hù)端。
  • 如果這個(gè) request 對(duì)應(yīng)的 location 上有設(shè)置 gzip_static on,那么就查找是否有對(duì)應(yīng)的.gz文件存在,有的話,就發(fā)送這個(gè)給客戶(hù)端(客戶(hù)端支持 gzip 的情況下)。
  • 請(qǐng)求的 URI 如果對(duì)應(yīng)一個(gè)靜態(tài)文件,static module 就發(fā)送靜態(tài)文件的內(nèi)容到客戶(hù)端。

內(nèi)容產(chǎn)生階段完成以后,生成的輸出會(huì)被傳遞到 filter 模塊去進(jìn)行處理。filter 模塊也是與 location 相關(guān)的。所有的 fiter 模塊都被組織成一條鏈。輸出會(huì)依次穿越所有的 filter,直到有一個(gè) filter 模塊的返回值表明已經(jīng)處理完成。

這里列舉幾個(gè)常見(jiàn)的 filter 模塊,例如:

  • server-side includes。
  • XSLT filtering。
  • 圖像縮放之類(lèi)的。
  • gzip 壓縮。

在所有的 filter 中,有幾個(gè) filter 模塊需要關(guān)注一下。按照調(diào)用的順序依次說(shuō)明如下:

  • write: 寫(xiě)輸出到客戶(hù)端,實(shí)際上是寫(xiě)到連接對(duì)應(yīng)的 socket 上。
  • postpone: 這個(gè) filter 是負(fù)責(zé) subrequest 的,也就是子請(qǐng)求的。
  • copy: 將一些需要復(fù)制的 buf(文件或者內(nèi)存)重新復(fù)制一份然后交給剩余的 body filter 處理。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)