摘要:深入?yún)f(xié)議從功能上來講,協(xié)議已經(jīng)完全能夠解決服務(wù)器與應(yīng)用之間的數(shù)據(jù)通信問題。消息頭信息主要的消息頭信息如下用于表示協(xié)議版本號。從服務(wù)器發(fā)送到應(yīng)用,表示中止一個(gè)處理中的請求。另外我們還需要明確一點(diǎn)就是服務(wù)器與進(jìn)程間通信是無序的。
本文首發(fā)于 深入剖析 Web 服務(wù)器與 PHP 應(yīng)用之間的通信機(jī)制 - 掌握 CGI 和 FastCGI 協(xié)議的運(yùn)行原理,轉(zhuǎn)載請注明出處!
身為一名使用 PHP 語言開發(fā)后端服務(wù)的程序猿,我們每天都和 PHP 以及 Web 服務(wù)器產(chǎn)生無數(shù)次的親密接觸。得益于它們,我們才能夠如此快速的構(gòu)建出令人陶醉的 Web 產(chǎn)品。
盡管我們已經(jīng)和 Web 服務(wù)器和 PHP 建立起深厚的友誼,但你知道它們之間為何能夠配合的如此默契么?
這一切都需要從 CGI(Common Gateway Interface:通用網(wǎng)關(guān)接口)協(xié)議說起。但是請不要對 CGI 協(xié)議產(chǎn)生任何的恐懼心理,它并非什么特別復(fù)雜的協(xié)議,如果你對它不甚了解,可能的原因或許是你還有花一點(diǎn)小心思來學(xué)習(xí)它。
所以,你應(yīng)該明白,現(xiàn)在你應(yīng)該抽出 20 多分鐘仔細(xì)的研究一下: Web 服務(wù)器與 PHP 應(yīng)用之間是如何進(jìn)行通信的這個(gè)問題。
介紹我們知道 PHP 自 5.4 起為我們內(nèi)置的 Web 服務(wù)器。不過在此之前的版本(或者不使用這個(gè)內(nèi)置服務(wù)器時(shí)),我們就需要使用其他的 Web 服務(wù)器,通常是 Nginx 或者 Apache 這兩塊 Web 服務(wù)器,來部署我們的 PHP 應(yīng)用。
這就涉及一個(gè)問題,當(dāng)用戶發(fā)起一個(gè) HTTP 請求后,我們的 PHP 應(yīng)用程序在處理這個(gè)請求時(shí)并沒有直接的解析這個(gè) HTTP 協(xié)議,而是可以直接從 $_GET、$_POST 和 $_SERVER等全局變量中,獲取到用戶請求數(shù)據(jù)和其它系統(tǒng)環(huán)境。這究竟又是為何呢?
要想整明白這個(gè)問題,我們就不得不需要整明白一個(gè)問題:CGI 協(xié)議。
CGI 協(xié)議同 HTTP 協(xié)議一樣是一個(gè)「應(yīng)用層」協(xié)議,它的 功能 是為了解決 Web 服務(wù)器與 PHP 應(yīng)用(或其他 Web 應(yīng)用)之間的通信問題。
既然它是一個(gè)「協(xié)議」,換言之它與語言無關(guān),即只要是實(shí)現(xiàn)類 CGI 協(xié)議的應(yīng)用就能夠?qū)崿F(xiàn)相互的通信。
深入 CGI 協(xié)議我們已經(jīng)知道了 CGI 協(xié)議是為了完成 Web 服務(wù)器和應(yīng)用之間進(jìn)行數(shù)據(jù)通信這個(gè)問題。那么,這一節(jié)我們就來看看究竟它們之間是如何進(jìn)行通信的。
簡單來講 CGI 協(xié)議它描述了 Web 服務(wù)器和應(yīng)用程序之間進(jìn)行數(shù)據(jù)傳輸?shù)母袷剑⑶抑灰覀兊木幊陶Z言支持標(biāo)準(zhǔn)輸入(STDIN)、標(biāo)準(zhǔn)輸出(STDOUT)以及環(huán)境變量等處理,你就可以使用它來編寫一個(gè) CGI 程序。
CGI 的運(yùn)行原理當(dāng)用戶訪問我們的 Web 應(yīng)用時(shí),會發(fā)起一個(gè) HTTP 請求。最終 Web 服務(wù)器接收到這個(gè)請求。
Web 服務(wù)器創(chuàng)建一個(gè)新的 CGI 進(jìn)程。在這個(gè)進(jìn)程中,將 HTTP 請求數(shù)據(jù)已一定格式解析出來,并通過標(biāo)準(zhǔn)輸入和環(huán)境變量傳入到 URL 指定的 CGI 程序(PHP 應(yīng)用 $_SERVER)。
Web 應(yīng)用程序處理完成后將返回?cái)?shù)據(jù)寫入到標(biāo)準(zhǔn)輸出中,Web 服務(wù)器進(jìn)程則從標(biāo)準(zhǔn)輸出流中讀取到響應(yīng),并采用 HTTP 協(xié)議返回給用戶響應(yīng)。
一句話就是 Web 服務(wù)器中的 CGI 進(jìn)程將接收到的 HTTP 請求數(shù)據(jù)讀取到環(huán)境變量中,通過標(biāo)準(zhǔn)輸入轉(zhuǎn)發(fā)給 PHP 的 CGI 程序;當(dāng) PHP 程序處理完成后,Web 服務(wù)器中的 CGI 進(jìn)程從標(biāo)準(zhǔn)輸出中讀取返回?cái)?shù)據(jù),并轉(zhuǎn)換回 HTTP 響應(yīng)消息格式,最終將頁面呈獻(xiàn)給用戶。然后 Web 服務(wù)器關(guān)閉掉這個(gè) CGI 進(jìn)程。
可以說 CGI 協(xié)議特別擅長處理 Web 服務(wù)器和 Web 應(yīng)用的通信問題。然而,它有一個(gè)嚴(yán)重缺陷,對于每個(gè)請求都需要重新 fork 出一個(gè) CGI 進(jìn)程,處理完成后立即關(guān)閉。
CGI 協(xié)議的缺陷每次處理用戶請求,都需要重新 fork CGI 子進(jìn)程、銷毀 CGI 子進(jìn)程。
一系列的 I/O 開銷降低了網(wǎng)絡(luò)的吞吐量,造成了資源的浪費(fèi),在大并發(fā)時(shí)會產(chǎn)生嚴(yán)重的性能問題。
深入 FastCGI 協(xié)議從功能上來講,CGI 協(xié)議已經(jīng)完全能夠解決 Web 服務(wù)器與 Web 應(yīng)用之間的數(shù)據(jù)通信問題。但是由于每個(gè)請求都需要重新 fork 出 CGI 子進(jìn)程導(dǎo)致性能堪憂,所以基于 CGI 協(xié)議的基礎(chǔ)上做了改進(jìn)便有了 FastCGI 協(xié)議,它是一種常駐型的 CGI 協(xié)議。
本質(zhì)上來將 FastCGI 和 CGI 協(xié)議幾乎完全一樣,它們都可以從 Web 服務(wù)器里接收到相同的數(shù)據(jù),不同之處在于采取了不同的通信方式。
再來回顧一下 CGI 協(xié)議每次接收到 HTTP 請求時(shí),都需要經(jīng)歷 fork 出 CGI 子進(jìn)程、執(zhí)行處理并銷毀 CGI 子進(jìn)程這一系列工作。
而 FastCGI 協(xié)議采用 進(jìn)程間通信(IPC) 來處理用戶的請求,下面我們就來看看它的運(yùn)行原理。
FastCGI 協(xié)議運(yùn)行原理FastCGI 進(jìn)程管理器啟動(dòng)時(shí)會創(chuàng)建一個(gè) 主(Master) 進(jìn)程和多個(gè) CGI 解釋器進(jìn)程(Worker 進(jìn)程),然后等待 Web 服務(wù)器的連接。
Web 服務(wù)器接收 HTTP 請求后,將 CGI 報(bào)文通過 套接字(UNIX 或 TCP Socket)進(jìn)行通信,將環(huán)境變量和請求數(shù)據(jù)寫入標(biāo)準(zhǔn)輸入,轉(zhuǎn)發(fā)到 CGI 解釋器進(jìn)程。
CGI 解釋器進(jìn)程完成處理后將標(biāo)準(zhǔn)輸出和錯(cuò)誤信息從同一連接返回給 Web 服務(wù)器。
CGI 解釋器進(jìn)程等待下一個(gè) HTTP 請求的到來。
為什么是 FastCGI 而非 CGI 協(xié)議如果僅僅因?yàn)楣ぷ髂J降牟煌?,似乎并沒有什么大不了的。并沒到非要選擇 FastCGI 協(xié)議不可的地步。
然而,對于這個(gè)看似微小的差異,但意義非凡,最終的結(jié)果是實(shí)現(xiàn)出來的 Web 應(yīng)用架構(gòu)上的差異。
CGI 與 FastCGI 架構(gòu)在 CGI 協(xié)議中,Web 應(yīng)用的生命周期完全依賴于 HTTP 請求的聲明周期。
對每個(gè)接收到的 HTTP 請求,都需要重啟一個(gè) CGI 進(jìn)程來進(jìn)行處理,處理完成后必須關(guān)閉 CGI 進(jìn)程,才能達(dá)到通知 Web 服務(wù)器本次 HTTP 請求處理完成的目的。
但是在 FastCGI 中完全不一樣。
FastCGI 進(jìn)程是常駐型的,一旦啟動(dòng)就可以處理所有的 HTTP 請求,而無需直接退出。
再看 FastCGI 協(xié)議通過前面的講解,我們相比已經(jīng)可以很準(zhǔn)確的說出來 FastCGI 是一種通信協(xié)議 這樣的結(jié)論?,F(xiàn)在,我們就將關(guān)注的焦點(diǎn)挪到協(xié)議本身,來看看這個(gè)協(xié)議的定義。
同 HTTP 協(xié)議一樣,F(xiàn)astCGI 協(xié)議也是有消息頭和消息體組成。
消息頭信息主要的消息頭信息如下:
Version:用于表示 FastCGI 協(xié)議版本號。
Type:用于標(biāo)識 FastCGI 消息的類型 - 用于指定處理這個(gè)消息的方法。
RequestID:標(biāo)識出當(dāng)前所屬的 FastCGI 請求。
Content Length: 數(shù)據(jù)包包體所占字節(jié)數(shù)。
消息類型定義BEGIN_REQUEST:從 Web 服務(wù)器發(fā)送到 Web 應(yīng)用,表示開始處理新的請求。
ABORT_REQUEST:從 Web 服務(wù)器發(fā)送到 Web 應(yīng)用,表示中止一個(gè)處理中的請求。比如,用戶在瀏覽器發(fā)起請求后按下瀏覽器上的「停止按鈕」時(shí),會觸發(fā)這個(gè)消息。
END_REQUEST:從 Web 應(yīng)用發(fā)送給 Web 服務(wù)器,表示該請求處理完成。返回?cái)?shù)據(jù)包里包含「返回的代碼」,它決定請求是否成功處理。
PARAMS:「流數(shù)據(jù)包」,從 Web 服務(wù)器發(fā)送到 Web 應(yīng)用。此時(shí)可以發(fā)送多個(gè)數(shù)據(jù)包。發(fā)送結(jié)束標(biāo)識為從 Web 服務(wù)器發(fā)出一個(gè)長度為 0 的空包。且 PARAMS 中的數(shù)據(jù)類型和 CGI 協(xié)議一致。即我們使用 $_SERVER 獲取到的系統(tǒng)環(huán)境等。
STDIN:「流數(shù)據(jù)包」,用于 Web 應(yīng)用從標(biāo)準(zhǔn)輸入中讀取出用戶提交的 POST 數(shù)據(jù)。
STDOUT:「流數(shù)據(jù)報(bào)」,從 Web 應(yīng)用寫入到標(biāo)準(zhǔn)輸出中,包含返回給用戶的數(shù)據(jù)。
Web 服務(wù)器和 FastCGI 交互過程Web 服務(wù)器接收用戶請求,但最終處理請求由 Web 應(yīng)用完成。此時(shí),Web 服務(wù)器嘗試通過套接字(UNIX 或 TCP 套接字,具體使用哪個(gè)由 Web 服務(wù)器配置決定)連接到 FastCGI 進(jìn)程。
FastCGI 進(jìn)程查看接收到的連接。選擇「接收」或「拒絕」連接。如果是「接收」連接,則從標(biāo)準(zhǔn)輸入流中讀取數(shù)據(jù)包。
如果 FastCGI 進(jìn)程在指定時(shí)間內(nèi)沒有成功接收到連接,則該請求失敗。否則,Web 服務(wù)器發(fā)送一個(gè)包含唯一的 RequestID 的 BEGIN_REQUEST 類型消息給到 FastCGI 進(jìn)程。后續(xù)所有數(shù)據(jù)包發(fā)送都包含這個(gè) RequestID。
然后,Web 服務(wù)器發(fā)送任意數(shù)量的 PARAMS 類型消息到 FastCGI 進(jìn)程。一旦發(fā)送完畢,Web 服務(wù)器通過發(fā)送一個(gè)空 PARAMS 消息包,然后關(guān)閉這個(gè)流。
另外,如果用戶發(fā)送了 POST 數(shù)據(jù) Web 服務(wù)器會將其寫入到 標(biāo)準(zhǔn)輸入(STDIN) 發(fā)送給 FastCGI 進(jìn)程。當(dāng)所有 POST 數(shù)據(jù)發(fā)送完成,會發(fā)送一個(gè)空的 標(biāo)準(zhǔn)輸入(STDIN) 來關(guān)閉這個(gè)流。
同時(shí),F(xiàn)astCGI 進(jìn)程接收到 BEGIN_REQUEST 類型數(shù)據(jù)包。它可以通過響應(yīng) END_REQUEST 來拒絕這個(gè)請求?;蛘呓邮詹⑻幚磉@個(gè)請求。如果接收請求,F(xiàn)astCGI 進(jìn)程會等待接收所有的 PARAMS 和 標(biāo)準(zhǔn)輸入數(shù)據(jù)包。
然后,在處理請求并將返回結(jié)果寫入 標(biāo)準(zhǔn)輸出(STDOUT) 流。處理完成后,發(fā)送一個(gè)空的數(shù)據(jù)包到標(biāo)準(zhǔn)輸出來關(guān)閉這個(gè)流,并且會發(fā)送一個(gè) END_REQUEST 類型消息通知 Web 服務(wù)器,告知它是否發(fā)生錯(cuò)誤異常。
如果是每個(gè)連接僅處理一個(gè)請求,發(fā)送 RequestID 則略顯多余。
但是我們的 Web 服務(wù)器和 FastCGI 進(jìn)程之間的連接可能處理多個(gè)請求,即一個(gè)連接可以處理多個(gè)請求。所以才需要采用數(shù)據(jù)包協(xié)議而不是直接使用單個(gè)數(shù)據(jù)流的原因:以實(shí)現(xiàn)「多路復(fù)用」。
因此,由于每個(gè)數(shù)據(jù)包都包含唯一的 RequestID,所以 Web 服務(wù)器才能在一個(gè)連接上發(fā)送任意數(shù)量的請求,并且 FastCGI 進(jìn)程也能夠從一個(gè)連接上接收到任意數(shù)量的請求數(shù)據(jù)包。
另外我們還需要明確一點(diǎn)就是 Web 服務(wù)器 與 FastCGI 進(jìn)程間通信是 無序的。即使我們在交互過程中看起來一個(gè)請求是有序的,但是我們的 Web 服務(wù)器也有可能在同一時(shí)間發(fā)出幾十個(gè) BEGIN_REQUEST 類型的數(shù)據(jù)包,以此類推。
PHP-FPM其實(shí)講解完 CGI 和 FastCGI 協(xié)議,基本上我們就已經(jīng)研究完 「Web 服務(wù)器與 PHP 應(yīng)用之間的通信機(jī)制」這個(gè)問題了。但是對于我們 PHP 軟件工程師來講,可能還會遇到「什么是 PHP-FPM」及其相關(guān)問題。這里我們一并來稍微講解一下。
PHP-FPM 是 FastCGI 進(jìn)程管理器(PHP FastCGI Process Manager),用于替換 PHP 內(nèi)核的 FastCGI 的大部分附加功能(或者說一種替代的 PHP FastCGI 實(shí)現(xiàn)),對于高負(fù)載網(wǎng)站是非常有用的。
下面是官網(wǎng)中獲取到的它所支持的特性:
支持平滑停止 / 啟動(dòng)的高級進(jìn)程管理功能;
可以工作于不同的 uid/gid/chroot 環(huán)境下,并監(jiān)聽不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的設(shè)置);
stdout 和 stderr 日志記錄;
在發(fā)生意外情況的時(shí)候能夠重新啟動(dòng)并緩存被破壞的 opcode;
文件上傳優(yōu)化支持;
"慢日志" - 記錄腳本(不僅記錄文件名,還記錄 PHP backtrace 信息,可以使用 ptrace 或者類似工具讀取和分析遠(yuǎn)程進(jìn)程的運(yùn)行數(shù)據(jù))運(yùn)行所導(dǎo)致的異常緩慢;
fastcgi_finish_request() - 特殊功能:用于在請求完成和刷新數(shù)據(jù)后,繼續(xù)在后臺執(zhí)行耗時(shí)的工作(錄入視頻轉(zhuǎn)換、統(tǒng)計(jì)處理等);
動(dòng)態(tài)/靜態(tài)子進(jìn)程產(chǎn)生;
基本 SAPI 運(yùn)行狀態(tài)信息(類似 Apache 的 mod_status);
基于 php.ini 的配置文件。
那么 PHP-FPM 是如何工作的呢?
PHP-FPM 進(jìn)程管理器有兩種進(jìn)程組成,一個(gè) Master 進(jìn)程和多個(gè) Worker 進(jìn)程。Master 進(jìn)程負(fù)責(zé)監(jiān)聽端口,接收來自 Web 服務(wù)器的請求,然后指派具體的 Worker 進(jìn)程處理請求;worker 進(jìn)程則一般有多個(gè) (依據(jù)配置決定進(jìn)程數(shù)),每個(gè)進(jìn)程內(nèi)部都嵌入了一個(gè) PHP 解釋器,用來執(zhí)行 PHP 代碼。
Nginx 服務(wù)器如何與 FastCGI 協(xié)同工作Nginx 服務(wù)器無法直接與 FastCGI 服務(wù)器進(jìn)行通信,需要啟用 ngx_http_fastcgi_module 模塊進(jìn)行代理配置,才能將請求發(fā)送給 FastCGI 服務(wù)。
其中,包括我們熟知的配置指令:
fastcgi_pass 用于設(shè)置 FastCGI 服務(wù)器的 IP 地址(TCT 套接字)或 UNIX 套接字。
fastcgi_param 設(shè)置傳入 FastCGI 服務(wù)器的參數(shù)。
你可以到 PHP FastCGI 實(shí)例教程 學(xué)習(xí)一些基本使用。
總結(jié)到這里我們基本就學(xué)習(xí)完 CGI、FastCGI、PHP-FPM以及 Nginx 服務(wù)器與 FastCGI 服務(wù)通信原理。一句話:
CGI 和 FastCGI 是一種協(xié)議和 HTTP 協(xié)議一樣位于應(yīng)用層,與語言無關(guān);PHP-FPM 是一種 FastCGI 協(xié)議的實(shí)現(xiàn),能夠管理 FastCGI 進(jìn)程。
擴(kuò)展閱讀https://blog.cuiyongjian.com/...
https://zhuanlan.zhihu.com/p/...
https://stackoverflow.com/que...
https://paper.seebug.org/289/
https://blog.csdn.net/shreck6...
http://www.phppan.com/2011/05...
https://github.com/pangudashu...
https://github.com/YuanLianDu...
http://blog.51cto.com/1358182...
http://haiyangxu.github.io/po...
https://www.cnblogs.com/xuewe...
https://www.zybuluo.com/phper...
https://www.awaimai.com/371.html
http://tiankonguse.com/blog/?...
https://www.digitalocean.com/...
http://www.whizkidtech.redpri...
https://fastcgi-archives.gith...
http://php.net/manual/zh/inst...
http://php.net/manual/zh/rese...
http://www.php-internals.com/...
https://www.yanxurui.cc/posts...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/29111.html
摘要:是與之間數(shù)據(jù)交換的一種協(xié)議。當(dāng)收到這個(gè)請求后,會啟動(dòng)對應(yīng)的程序,這里就是的解析器。接下來解析器會解析文件,初始化執(zhí)行環(huán)境,然后處理請求,再以規(guī)定規(guī)定的格式返回處理后的結(jié)果,退出進(jìn)程,再把結(jié)果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數(shù)據(jù)交換的一種協(xié)議。FastCGI:同 CGI,是一種通信協(xié)議,但比 CGI 在效率上做了一些優(yōu)化。PHP-...
摘要:是與之間數(shù)據(jù)交換的一種協(xié)議。當(dāng)收到這個(gè)請求后,會啟動(dòng)對應(yīng)的程序,這里就是的解析器。接下來解析器會解析文件,初始化執(zhí)行環(huán)境,然后處理請求,再以規(guī)定規(guī)定的格式返回處理后的結(jié)果,退出進(jìn)程,再把結(jié)果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數(shù)據(jù)交換的一種協(xié)議。FastCGI:同 CGI,是一種通信協(xié)議,但比 CGI 在效率上做了一些優(yōu)化。PHP-...
摘要:是與之間數(shù)據(jù)交換的一種協(xié)議。當(dāng)收到這個(gè)請求后,會啟動(dòng)對應(yīng)的程序,這里就是的解析器。接下來解析器會解析文件,初始化執(zhí)行環(huán)境,然后處理請求,再以規(guī)定規(guī)定的格式返回處理后的結(jié)果,退出進(jìn)程,再把結(jié)果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數(shù)據(jù)交換的一種協(xié)議。FastCGI:同 CGI,是一種通信協(xié)議,但比 CGI 在效率上做了一些優(yōu)化。PHP-...
摘要:要說與是如何協(xié)同工作的,首先得說和這兩個(gè)協(xié)議。之于標(biāo)準(zhǔn)的,也提供了一些增強(qiáng)功能,具體可以參考官方文檔。為了能夠使理解協(xié)議,提供了模塊來將請求映射為對應(yīng)的請求。 網(wǎng)絡(luò)上有很多關(guān)于如何配置 Nginx + FPM 的文章,但它們更多從操作的角度出發(fā),告訴我們怎么做,但卻沒有告訴我們?yōu)槭裁匆@么做,本文從 Nginx 與 FPM 的工作機(jī)制出發(fā),探討配置背后的原理,讓我們真正理解 Nginx...
摘要:要說與是如何協(xié)同工作的,首先得說和這兩個(gè)協(xié)議。之于標(biāo)準(zhǔn)的,也提供了一些增強(qiáng)功能,具體可以參考官方文檔。為了能夠使理解協(xié)議,提供了模塊來將請求映射為對應(yīng)的請求。 網(wǎng)絡(luò)上有很多關(guān)于如何配置 Nginx + FPM 的文章,但它們更多從操作的角度出發(fā),告訴我們怎么做,但卻沒有告訴我們?yōu)槭裁匆@么做,本文從 Nginx 與 FPM 的工作機(jī)制出發(fā),探討配置背后的原理,讓我們真正理解 Nginx...
閱讀 2078·2021-10-12 10:12
閱讀 794·2021-09-24 09:47
閱讀 1195·2021-08-19 11:12
閱讀 3482·2019-08-29 13:06
閱讀 691·2019-08-26 11:43
閱讀 2578·2019-08-23 17:20
閱讀 1155·2019-08-23 16:52
閱讀 2607·2019-08-23 14:27