CGI 和 FastCGI 協議的運行原理

介紹

在用PHP開發的過程當中,咱們經常使用Nginx或者Apache做爲咱們的Web服務器。可是PHP是如何與這些Web服務器通訊的呢?nginx

  • Apache把PHP做爲一個模塊集成到Apache進程(httpd)運行,這種mod_php的運行模式與PHP-CGI沒有任何關係。web

  • Nginx是經過FastCGI來實現與PHP的通訊。數據庫

要談FastCGI就必須先說說CGI。那什麼是CGI?編程

CGI(Common Gateway Interface:通用網關接口)是Web 服務器運行時外部程序的規範,按CGI 編寫的程序能夠擴展服務器功能。CGI 應用程序能與瀏覽器進行交互,還可經過數據庫API 與數據庫服務器等外部數據源進行通訊,從數據庫服務器中獲取數據。--百度百科瀏覽器

CGI協議同 HTTP 協議同樣是一個「應用層」協議,它的 功能 是爲了解決 Web 服務器與 PHP 應用(或其餘 Web 應用)之間的通訊問題。服務器

既然它是一個「協議」,換言之它與語言無關,即只要是實現類 CGI 協議的應用就可以實現相互的通訊。網絡

深刻CGI協議

咱們已經知道了 CGI 協議是爲了完成 Web 服務器和應用之間進行數據通訊這個問題。那麼,這一節咱們就來看看究竟它們之間是如何進行通訊的。架構

簡單來說 CGI 協議它描述了 Web 服務器和應用程序之間進行數據傳輸的格式,而且只要咱們的編程語言支持標準輸入(STDIN)、標準輸出(STDOUT)以及環境變量等處理,你就可使用它來編寫一個 CGI 程序。併發

CGI的運行原理

  • 當用戶訪問咱們的 Web 應用時,會發起一個 HTTP 請求。最終 Web 服務器接收到這個請求。

  • Web 服務器建立一個新的 CGI 進程。在這個進程中,將 HTTP 請求數據已必定格式解析出來,並經過標準輸入和環境變量傳入到 URL 指定的 CGI 程序(PHP 應用 $_SERVER)。

  • Web 應用程序處理完成後將返回數據寫入到標準輸出中,Web 服務器進程則從標準輸出流中讀取到響應,並採用 HTTP 協議返回給用戶響應。

一句話就是 Web 服務器中的 CGI 進程將接收到的 HTTP 請求數據讀取到環境變量中,經過標準輸入轉發給 PHP 的 CGI 程序;當 PHP 程序處理完成後,Web 服務器中的 CGI 進程從標準輸出中讀取返回數據,並轉換回 HTTP 響應消息格式,最終將頁面呈獻給用戶。而後 Web 服務器關閉掉這個 CGI 進程。

能夠說 CGI 協議特別擅長處理 Web 服務器和 Web 應用的通訊問題。然而,它有一個嚴重缺陷,對於每一個請求都須要從新 fork 出一個 CGI 進程,處理完成後當即關閉。

CGI協議的缺陷

  • 每次處理用戶請求,都須要從新 fork CGI 子進程、銷燬 CGI 子進程。

  • 一系列的 I/O 開銷下降了網絡的吞吐量,形成了資源的浪費,在大併發時會產生嚴重的性能問題。

深刻FastCGI協議

從功能上來說,CGI 協議已經徹底可以解決 Web 服務器與 Web 應用之間的數據通訊問題。可是因爲每一個請求都須要從新 fork 出 CGI 子進程致使性能堪憂,因此基於 CGI 協議的基礎上作了改進便有了 FastCGI 協議,它是一種常駐型的 CGI 協議。

本質上來將 FastCGI 和 CGI 協議幾乎徹底同樣,它們均可以從 Web 服務器裏接收到相同的數據,不一樣之處在於採起了不一樣的通訊方式。

再來回顧一下 CGI 協議每次接收到 HTTP 請求時,都須要經歷 fork 出 CGI 子進程、執行處理並銷燬 CGI 子進程這一系列工做。

FastCGI 協議採用 進程間通訊(IPC) 來處理用戶的請求,下面咱們就來看看它的運行原理。

FastCGI協議運行原理

  • FastCGI 進程管理器啓動時會建立一個 主(Master) 進程和多個 CGI 解釋器進程(Worker 進程),而後等待 Web 服務器的鏈接。

  • Web 服務器接收 HTTP 請求後,將 CGI 報文經過 套接字(UNIX 或 TCP Socket)進行通訊,將環境變量和請求數據寫入標準輸入,轉發到 CGI 解釋器進程。

  • CGI 解釋器進程完成處理後將標準輸出和錯誤信息從同一鏈接返回給 Web 服務器。

  • CGI 解釋器進程等待下一個 HTTP 請求的到來。

爲何是 FastCGI 而非 CGI 協議

若是僅僅由於工做模式的不一樣,彷佛並無什麼大不了的。並沒到非要選擇 FastCGI 協議不可的地步。

然而,對於這個看似微小的差別,但意義非凡,最終的結果是實現出來的 Web 應用架構上的差別。

CGI 與 FastCGI 架構

在 CGI 協議中,Web 應用的生命週期徹底依賴於 HTTP 請求的聲明週期。

對每一個接收到的 HTTP 請求,都須要重啓一個 CGI 進程來進行處理,處理完成後必須關閉 CGI 進程,才能達到通知 Web 服務器本次 HTTP 請求處理完成的目的。

可是在 FastCGI 中徹底不同。

FastCGI 進程是常駐型的,一旦啓動就能夠處理全部的 HTTP 請求,而無需直接退出。

再看 FastCGI 協議

經過前面的講解,咱們相比已經能夠很準確的說出來 FastCGI 是一種通訊協議 這樣的結論。如今,咱們就將關注的焦點挪到協議自己,來看看這個協議的定義。

同 HTTP 協議同樣,FastCGI 協議也是有消息頭和消息體組成。

消息頭信息

主要的消息頭信息以下:

  • Version: 用於表示 FastCGI 協議版本號。

  • Type: 用於標識 FastCGI 消息的類型 - 用於指定處理這個消息的方法。

  • RequestID: 標識出當前所屬的 FastCGI 請求。

  • Content Length: 數據包包體所佔字節數。

消息類型定義

  • BEGIN_REQUEST: 從 Web 服務器發送到 Web 應用,表示開始處理新的請求。

  • ABORT_REQUEST: 從 Web 服務器發送到 Web 應用,表示停止一個處理中的請求。好比,用戶在瀏覽器發起請求後按下瀏覽器上的「中止按鈕」時,會觸發這個消息。

  • END_REQUEST: 從 Web 應用發送給 Web 服務器,表示該請求處理完成。返回數據包裏包含「返回的代碼」,它決定請求是否成功處理。

  • PARAMS: 「流數據包」,從 Web 服務器發送到 Web 應用。此時能夠發送多個數據包。發送結束標識爲從 Web 服務器發出一個長度爲 0 的空包。且 PARAMS 中的數據類型和 CGI 協議一致。即咱們使用 $_SERVER 獲取到的系統環境等。

  • STDIN: 「流數據包」,用於 Web 應用從標準輸入中讀取出用戶提交的 POST 數據。

  • STDOUT: 「流數據報」,從 Web 應用寫入到標準輸出中,包含返回給用戶的數據。

Web 服務器和 FastCGI 交互過程

  • Web 服務器接收用戶請求,但最終處理請求由 Web 應用完成。此時,Web 服務器嘗試經過套接字(UNIX 或 TCP 套接字,具體使用哪一個由 Web 服務器配置決定)鏈接到 FastCGI 進程。

  • FastCGI 進程查看接收到的鏈接。選擇「接收」或「拒絕」鏈接。若是是「接收」鏈接,則從標準輸入流中讀取數據包。

  • 若是 FastCGI 進程在指定時間內沒有成功接收到鏈接,則該請求失敗。不然,Web 服務器發送一個包含惟一的RequestID 的 BEGIN_REQUEST 類型消息給到 FastCGI 進程。後續全部數據包發送都包含這個 RequestID。 而後,Web 服務器發送任意數量的 PARAMS 類型消息到 FastCGI 進程。一旦發送完畢,Web 服務器經過發送一個空PARAMS 消息包,而後關閉這個流。 另外,若是用戶發送了 POST 數據 Web 服務器會將其寫入到 標準輸入(STDIN) 發送給 FastCGI 進程。當全部 POST 數據發送完成,會發送一個空的 標準輸入(STDIN) 來關閉這個流。

  • 同時,FastCGI 進程接收到 BEGINREQUEST 類型數據包。它能夠經過響應 ENDREQUEST 來拒絕這個請求。或者接收並處理這個請求。若是接收請求,FastCGI 進程會等待接收全部的 PARAMS 和 標準輸入數據包。 而後,在處理請求並將返回結果寫入 標準輸出(STDOUT) 流。處理完成後,發送一個空的數據包到標準輸出來關閉這個流,而且會發送一個 END_REQUEST 類型消息通知 Web 服務器,告知它是否發生錯誤異常。

爲何須要在消息頭髮送 RequestID 這個標識?

若是是每一個鏈接僅處理一個請求,發送 RequestID 則略顯多餘。

可是咱們的 Web 服務器和 FastCGI 進程之間的鏈接可能處理多個請求,即一個鏈接能夠處理多個請求。因此才須要採用數據包協議而不是直接使用單個數據流的緣由:以實現「多路複用」。

所以,因爲每一個數據包都包含惟一的 RequestID,因此 Web 服務器才能在一個鏈接上發送任意數量的請求,而且 FastCGI 進程也可以從一個鏈接上接收到任意數量的請求數據包。

另外咱們還須要明確一點就是 Web 服務器 與 FastCGI 進程間通訊是 無序的。即便咱們在交互過程當中看起來一個請求是有序的,可是咱們的 Web 服務器也有可能在同一時間發出幾十個 BEGIN_REQUEST 類型的數據包,以此類推。

PHP-FPM

PHP-FPM即PHP-FastCGI Process Manager.

PHP-FPM是FastCGI的實現,並提供了進程管理的功能。

進程包含 master 進程和 worker 進程兩種進程。

master 進程只有一個,負責監聽端口,接收來自 Web Server 的請求,而 worker 進程則通常有多個(具體數量根據實際須要配置),每一個進程內部都嵌入了一個 PHP 解釋器,是 PHP 代碼真正執行的地方。

PHP-FPM 是 FastCGI 進程管理器(PHP FastCGI Process Manager)(http://php.net/manual/zh/install.fpm.php),用於替換 PHP 內核的 FastCGI 的大部分附加功能(或者說一種替代的 PHP FastCGI 實現),對於高負載網站是很是有用的。

PHP-FPM如何工做的?

PHP-FPM 進程管理器有兩種進程組成,一個 Master 進程和多個 Worker 進程。Master 進程負責監聽端口,接收來自 Web 服務器的請求,而後指派具體的 Worker 進程處理請求;worker 進程則通常有多個 (依據配置決定進程數),每一個進程內部都嵌入了一個 PHP 解釋器,用來執行 PHP 代碼。

Nginx 服務器如何與 FastCGI 協同工做

Nginx 服務器沒法直接與 FastCGI 服務器進行通訊,須要啓用 ngx_http_fastcgi_module 模塊進行代理配置,才能將請求發送給 FastCGI 服務。

轉載:

PHP和Apache是如何通訊的?

Nginx+PHP-FPM運行原理詳解

掌握CGI和FastCGI協議的運行原理

相關文章
相關標籤/搜索