常見的網關協議
在工作中一直使用PHP,但知道怎么開車,不知道車是怎么跑的是不行的,不然出問題了也不知道出在哪里,因此了解下PHP-FPM的運行原理。、
CGI
通用網絡接口(Common Gateway Interface),是用來規定Web Server和應用程序直接進行數據數據傳輸的協議。可以用任意語言來實現,只要我們使用的語言的能夠支持標准輸入、標准輸出及環境變量等處理,就能夠開發出一個CGI程序。
CGI的運行過程:
- 用戶訪問Web應用,發起一個HTTP請求,Web服務受到該請求;
- HTTP服務器將請求數據從HTTP請求中解析出來,根據請求的URL創建一個新的CGI進程,並通過環境變量和標准輸入傳入到CGI進程中;
- CGI進程根據請求進行邏輯處理,連接其他服務請求數據或者從DB獲取數據或者只是單純的進行邏輯運算;
- CGI進程將處理的結果寫入到標准輸出中;
- Web服務器從標准輸出中讀取相應,並返回給用戶;
- Web服務器關閉CGI進程。
從上面可以看出,對於每一個用戶請求,都需要重新fork一個子進程處理請求、請求結束銷毀子進程的過程,如果是少量請求,如果是大並發,這種fork-and-execute
顯然不適合,這是就出現了下面要講的fastcgi
模式。
FastCGI模式
快速通用網關接口(Fast Common Gateway Interface/FastCGI)是CGI的改進版,FastCGI的提出旨在減少Web服務器與CGI進程交互的開銷,使得服務器能夠服務更多的請求。與CGI每次創建一個子進程不同的是,FastCGI的CGI進程在服務器啟動的時候就已經創建好了(常駐形式),並由FastCGI進程管理器管理,而不是由Web服務器創建。並且,與CGI通過標准輸入和標准輸出進行通信不同的是,Web服務器和FastCGI進程之間是通過IPC(進程間通信)來處理用戶的請求的。
FastCGI處理請求的過程:
- Web Server啟動時載入FastCGI進程管理器;
- FastCGI進程管理器進行自身初始化,並根據配置啟動多個CGI進程並循環等待來自Web Server的請求;
- 用戶訪問Web應用,發起一個HTTP請求,Web接受到請求后,將請求通過IPC通信轉發一個CGI進程,Web Server將環境變量和請求數據寫入到標准輸出(流數據包),由CGI進程讀取。
- CGI進程完成處理后將結果輸出到標准輸出(流數據包),然后接續等待下一個請求;
- Web Server從標准輸出讀取請求結果並返回給用戶。
FastCGI通信協議
同HTTP一樣,FastCGI由消息頭和消息體組成
主要消息頭如下:
消息頭 | 作用 |
---|---|
Version | 協議版本號 |
Type | 消息的類型,用於指定指定處理這個消息的方法 |
RequestID | 標識當前所屬的FastCGI請求,如果一個連接僅處理一個請求,RequestID存在的意義就不大了 |
Content Length | 數據包包體所占字節數 |
消息類型定義:
消息類型 | 描述 |
---|---|
BEGIN_REUQEST | 從Web Server發往CGI,表示開始處理新的請求,其中帶RequestID |
ABORT_REQUEST | 從Web Server發往CGI,表示終止一個處理中的請求 |
END_REQUESST | CGI發送給Web Server,表示該請求完成, 返回數據里包含返回代碼,決定請求是否成功處理 |
PARAMS | 從Web Server發往CGI,可以發送多個數據包, 發送接收標識為一個長度為0的空包,該中的數據類型和CGI協議一致 |
STDIN | 從Web Server發往CGI,用於CGI從中讀取用戶提交的POST數據 |
STDOUT | 從Web Server發往CGI,包含返回給用戶的數據 |
具體交互過程:
- Web Server接收用戶請求,然后將請求轉發給FastCGI進程;
- FastCgi進程決定是否處理請求,如果接收請求,則從連接中讀取數據;
- Web Server發送包含
RequestID
的BEGIN_REQUEST
類型消息給CGI進程,后續所有的數據包都會帶上該RequestID
,然后Web Server發送任一的PARAMS
類型消息到FastCGI進程,發送接收后,Web服務器發送一個空的PARAMS消息包,表示PARAMS
類型消息發送完畢,如果包含POST數據,Web Server會通過STDIN
消息類型發送給FastCGI進程,當POST內容發送完畢后,會發送一個空的STDIN
消息包; - CGI進程從連接中讀取相關數據,如果拒絕請求則發送
ENDQUEST
請求,否則讀取請求數據,進行處理,將處理結果通過STDOUT
消息類型發送給Web Server,並通過ENDREQUEST
告知Web Server處理的結果。
PHP-FPM

PHP-FPM(PHP-FastCGI Process Manager),是PHP FastCGI的實現,提供了進程管理的功能。
PHP-FPM由兩種進程組成:Master進程和Worker進程,一個PHP-FPM通常只有一個Master進程,用來管理Worker進程,但其中一個子進程異常退出后,能夠重啟新的子進程,還負責其他管理功能,如平滑啟動等;一個PHP-FPM通常存在多個Worker進程,所有的Worker進程監聽同一個端口,當一個請求進來時,誰搶到就誰負責處理,每個進程內嵌一個PHP解釋器,用來執行PHP代碼。
Nginx和PHP-FPM
通過指令fastcig_pass
指定fastcgi監聽的地址(TCP/UDS),Nginx在接收相關請求時,會將請求通過改地址進行轉發。
WSGI
Web Server Gateway Interface,是用在python web框架編寫的Web Server和Web Application之間通信的規范,要實現WSGI協議,必須同時實現Web Server和Web Application。
有了WSGI協議,只要server和application都遵循WSGI協議,那么就可以隨意的組合。比如,uWSGI和Gunicor是實現了WSGI協議的Server端,Django、Flask是實現了WSGI協議的Application端,那么實際使用時,可以隨意搭配使用。
實現簡單的WSGI服務
Web Application
一個可調用對象(可以使方法/函數/類/對象,只要有obj.__call__
即可)
接受2個參數:
- 字典
environ
,包含環境變量; - 回調函數
start_response()
,用來發送HTTP相應(包括HTTP狀態碼、HTTP返回報頭)
Applicatin
層無需關心environ
和start_response()
是如何實現的,只需要直接使用它們即可。
返回:可迭代對象,內容是返回包的內容(字符串)

Web Server
Web Server的作用就是構建一個可以讓Application成功執行的環境,從輸入獲取正確的請求並解析,傳遞給Application,然后返回正確的Response。
Server
層無需關心application
對象是如何運作的,只需要傳入什么和會返回什么即可。

Web Middle
除了Application
和Server
,wsgi
協議還規范了Midlle
層,Middle
介於Application
和Server
之間,Middle
對於Application
和Server
來說是透明的,對於Application
來說,Middle
就是Server
;對於Server
來說,Middle
就是Application
。例如,服務器拿到了客戶端請求的URL, 根據目標URL,將請求消息路由到不同的應用對象。在Python中可以實現為裝飾器。如下,是一個簡單的實現:

常見的Python web框架都實現了wsgi server
,但性能並不是很好,常常僅適用於開發環境;在生產環境中,使用最廣的還是Gunicor
和uWSGI
。
與uWSGI/uwsgi的區別
uwsgi
: 一種線路協議而不是通信協議,在此常用於在uWSGI服務器與其他網絡服務器的數據通信,其中Nginx有插件支持該協議uWSGI
:一個web服務器,實現了WSGI協議、uwsgi協議、http協議等,對外可以使用uwsgi
進行通信,對內使用WSGI
協議調用具體的Application
。
Refer