目錄php
在用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 協議是爲了完成 Web 服務器和應用之間進行數據通訊這個問題。那麼,這一節咱們就來看看究竟它們之間是如何進行通訊的。架構
簡單來說 CGI 協議它描述了 Web 服務器和應用程序之間進行數據傳輸的格式,而且只要咱們的編程語言支持標準輸入(STDIN)、標準輸出(STDOUT)以及環境變量等處理,你就可使用它來編寫一個 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 進程,處理完成後當即關閉。
每次處理用戶請求,都須要從新 fork CGI 子進程、銷燬 CGI 子進程。
一系列的 I/O 開銷下降了網絡的吞吐量,形成了資源的浪費,在大併發時會產生嚴重的性能問題。
從功能上來說,CGI
協議已經徹底可以解決 Web 服務器與 Web 應用之間的數據通訊問題。可是因爲每一個請求都須要從新 fork 出 CGI 子進程致使性能堪憂,因此基於 CGI
協議的基礎上作了改進便有了 FastCGI
協議,它是一種常駐型的 CGI 協議。
本質上來將 FastCGI 和 CGI 協議幾乎徹底同樣,它們均可以從 Web 服務器裏接收到相同的數據,不一樣之處在於採起了不一樣的通訊方式。
再來回顧一下 CGI 協議每次接收到 HTTP 請求時,都須要經歷 fork 出 CGI 子進程、執行處理並銷燬 CGI 子進程這一系列工做。
而 FastCGI
協議採用 進程間通訊(IPC) 來處理用戶的請求,下面咱們就來看看它的運行原理。
FastCGI 進程管理器啓動時會建立一個 主(Master) 進程和多個 CGI 解釋器進程(Worker 進程),而後等待 Web 服務器的鏈接。
Web 服務器接收 HTTP 請求後,將 CGI 報文經過 套接字(UNIX 或 TCP Socket)進行通訊,將環境變量和請求數據寫入標準輸入,轉發到 CGI 解釋器進程。
CGI 解釋器進程完成處理後將標準輸出和錯誤信息從同一鏈接返回給 Web 服務器。
CGI 解釋器進程等待下一個 HTTP 請求的到來。
若是僅僅由於工做模式的不一樣,彷佛並無什麼大不了的。並沒到非要選擇 FastCGI 協議不可的地步。
然而,對於這個看似微小的差別,但意義非凡,最終的結果是實現出來的 Web 應用架構上的差別。
在 CGI 協議中,Web 應用的生命週期徹底依賴於 HTTP 請求的聲明週期。
對每一個接收到的 HTTP 請求,都須要重啓一個 CGI 進程來進行處理,處理完成後必須關閉 CGI 進程,才能達到通知 Web 服務器本次 HTTP 請求處理完成的目的。
可是在 FastCGI 中徹底不同。
FastCGI 進程是常駐型的,一旦啓動就能夠處理全部的 HTTP 請求,而無需直接退出。
經過前面的講解,咱們相比已經能夠很準確的說出來 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 服務器接收用戶請求,但最終處理請求由 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 則略顯多餘。
可是咱們的 Web 服務器和 FastCGI 進程之間的鏈接可能處理多個請求,即一個鏈接能夠處理多個請求。因此才須要採用數據包協議而不是直接使用單個數據流的緣由:以實現「多路複用」。
所以,因爲每一個數據包都包含惟一的 RequestID,因此 Web 服務器才能在一個鏈接上發送任意數量的請求,而且 FastCGI 進程也可以從一個鏈接上接收到任意數量的請求數據包。
另外咱們還須要明確一點就是 Web 服務器 與 FastCGI 進程間通訊是 無序的。即便咱們在交互過程當中看起來一個請求是有序的,可是咱們的 Web 服務器也有可能在同一時間發出幾十個 BEGIN_REQUEST 類型的數據包,以此類推。
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 進程管理器有兩種進程組成,一個 Master 進程和多個 Worker 進程。Master 進程負責監聽端口,接收來自 Web 服務器的請求,而後指派具體的 Worker 進程處理請求;worker 進程則通常有多個 (依據配置決定進程數),每一個進程內部都嵌入了一個 PHP 解釋器,用來執行 PHP 代碼。
Nginx 服務器沒法直接與 FastCGI 服務器進行通訊,須要啓用 ngx_http_fastcgi_module 模塊進行代理配置,才能將請求發送給 FastCGI 服務。
轉載: