如何通俗地解釋 CGI、FastCGI、php-fpm 之間的關係?

在apache里,可以以php5_module模塊的方式調用PHP,而在nginx裡面,則需要通過php-fpm來調用,這兩種調用方式有什麼不同?另外,它們跟cgi,fastcgi有什麼關係?


上面的回答多少都有些問題吧。

CGI是HTTP Server和一個獨立的進程之間的協議,把HTTP Request的Header設置成進程的環境變數,HTTP Request的正文設置成進程的標準輸入,而進程的標準輸出就是HTTP Response包括Header和正文。

FASTCGI是和HTTP協議類似的概念。無非就是規定了在同一個TCP連接里怎麼同時傳多個HTTP連接。這實際上導致了個問題,有個HTTP連接傳個大文件不肯讓出FASTCGI連接,在同一個FASTCGI連接里的其他HTTP連接就傻了。所以Lighttpd? 引入了 X-SENDFILE 。

php-fpm就相當於是Apache+mod_php。無非php-fpm自帶了FASTCGI Server,而Apache是HTTP Server。

那個WSGI和這個問題沒啥關係吧。WSGI這個只是Python內部的一個介面。無論你前面是FASTCGI,HTTP,SCGI,uWSGI等協議,你的FASTCGI/HTTP/SCGI/uWSGI Server都以相同的參數格式去調用一個函數,這樣你用Python寫的Web應用並不需要修改代碼,就可以運行在不同的Server後面了。無非CGI協議是進程間的,而WSGI是進程內的。


來源: 說說http/webserver/fastcgi/php-fpm

講Fastcgi之前需要先講CGI,CGI是為了保證web server傳遞過來的數據是標準格式的,它是一個協議,方便CGI程序的編寫者。Fastcgi是CGI的更高級的一種方式,是用來提高CGI程序性能的。

web server(如nginx)只是內容的分發者。比如,如果請求/index.html,那麼web server會去文件系統中找到這個文件,發送給瀏覽器,這裡分發的是靜態資源。

如果現在請求的是/index.php,根據配置文件,nginx知道這個不是靜態文件,需要去找PHP解析器來處理,那麼他會把這個請求簡單處理後交給PHP解析器。此時CGI便是規定了要傳什麼數據/以什麼格式傳輸給php解析器的協議。

當web server收到/index.php這個請求後,會啟動對應的CGI程序,這裡就是PHP的解析器。接下來PHP解析器會解析php.ini文件,初始化執行環境,然後處理請求,再以CGI規定的格式返回處理後的結果,退出進程。web server再把結果返回給瀏覽器。

那麼CGI相較於Fastcgi而言其性能瓶頸在哪呢?CGI針對每個http請求都是fork一個新進程來進行處理,處理過程包括解析php.ini文件,初始化執行環境等,然後這個進程會把處理完的數據返回給web伺服器,最後web伺服器把內容發送給用戶,剛才fork的進程也隨之退出。 如果下次用戶還請求動態資源,那麼web伺服器又再次fork一個新進程,周而復始的進行。

而Fastcgi則會先fork一個master,解析配置文件,初始化執行環境,然後再fork多個worker。當請求過來時,master會傳遞給一個worker,然後立即可以接受下一個請求。這樣就避免了重複的勞動,效率自然是高。而且當worker不夠用時,master可以根據配置預先啟動幾個worker等著;當然空閑worker太多時,也會停掉一些,這樣就提高了性能,也節約了資源。這就是Fastcgi的對進程的管理。大多數Fastcgi實現都會維護一個進程池。註:swoole作為httpserver,實際上也是類似這樣的工作方式。

那PHP-FPM又是什麼呢?它是一個實現了Fastcgi協議的程序,用來管理Fastcgi起的進程的,即能夠調度php-cgi進程的程序。現已在PHP內核中就集成了PHP-FPM,使用--enalbe-fpm這個編譯參數即可。另外,修改了php.ini配置文件後,沒辦法平滑重啟,需要重啟php-fpm才可。此時新fork的worker會用新的配置,已經存在的worker繼續處理完手上的活。


通俗的可以把伺服器看作餐廳,用戶請求看作來用餐的顧客,伺服器處理請求看作解決顧客的就餐問題(響應輸出一份飯)。

伺服器上靜態資源看作已做好的飯,只要放到餐盒裡就可以返回給顧客,動態資源需要廚房大廚現成做份再放到餐盒裡返回給顧客。

php_mod這個大廚有個特點,看見有顧客進門就點火,不管顧客要不要現做的,有點浪費資源

php_fpm這個大廚有好多小弟一直點著火(多個處理進程),等有顧客說要現做,大廚就安排小弟做份返回給客戶

cgi也是個大廚,不過他等到顧客要現做,他才點火,做飯,然後熄火。等待下一個要現做的到來

fastcgi呢就是個大廚雇了一幫小弟,專門做需要現場做的飯,大廚只管分派任務,小弟真正操鍋做飯


CGI(Common Gateway Interface)

最初,CGI 是在 1993 年由美國國家超級電腦應用中心(NCSA)為 NCSA HTTPd Web 伺服器開發的。

這個 Web 伺服器使用了 UNIX shell 環境變數 來保存從 Web 伺服器傳遞出去的參數,然後生成一個運行 CGI 的獨立進程。CGI的第一個實現是 Perl 寫的[1]。

  • 效率低下:每一個連接 fork 一個進程處理。
  • 功能十分有限:CGI只能收到一個請求,輸出一個響應。很難在CGI體系去對Web請求的控制,例如:用戶認證等。

正因為這些問題,在CGI誕生後的很長一段時間,各種Web Server都還是採用API這種強綁定的方式去支持Web開發,其中Apache的mod_php就屬於這種方式。所以後面就有大神提出了FastCGI標準。

FastCGI(Fast Common Gateway Interface)

FastCGI使用進程/線程池來處理一連串的請求。這些進程/線程由FastCGI伺服器管理,而不是Web伺服器。 當進來一個請求時,Web伺服器把環境變數和這個頁面請求通過一個Socket長連接傳遞給FastCGI進程。所以FastCGI有如下的優點:

  • 性能:通過進程/線程池規避了CGI開闢新的進程的開銷。
  • 兼容:非常容易改造現有CGI標準的程序。
  • 語言無關:FastCGI是一套標準,理論上講只要能進行標準輸出(stdout)的語言都可以作為FastCGI標準的Web後端。


    下面是一個簡單FastCGI後端的偽代碼

void main(void)
{
int count = 0;
while(FCGI_Accept() &>= 0) {
printf(「Content-type: text/html
」);
printf(「
」);
printf(「Hello world!
」);
printf(「Request number %d.」, count++);
}
exit(0);
}

  • Web Server隔離:FastCGI後端和Web Server運行在不同的進程中,後端的任何故障不會導致Web Server掛掉。
  • 專利:沒有Apache mod_php之類的私有API的知識產權問題。
  • 擴展:FastCGI後端和Web Server通過Socket進行通信,兩者可以分散式部署並方便進行橫向擴展。

所以FastCGI一推出就幾乎獲得了所有主流Web Server的支持,Apache、Lighttpd、IIS、Cherokee……

題主說的php-fpm就是一種FastCGI的後端實現。

But,事情總是還有改進的餘地的,FastCGI這套工作模式實際上沒有什麼太大缺陷,但是有些不安分的Python程序猿覺得,FastCGI標準下寫非同步的Web服務還是不太方便,如果能夠收到請求後CGI端去處理,處理完畢後通過Callback回調來返回結果,那樣豈不是很Coooool?!所以WSGI就被創造出來了:

WSGI(Web Server Gateway Interface)

Web伺服器網關介面(Web Server Gateway Interface,縮寫為WSGI)是為Python語言定義的Web伺服器和Web應用程序或框架之間的一種簡單而通用的介面。

當Web Server收到一個請求後,可以通過Socket把環境變數和一個Callback回調函數傳給後端Web應用,Web應用在完成頁面組裝後通過Callback把內容返回給Web Server。這樣做的優點有很多:

  • 非同步化,通過Callback將Web請求的工作拆解開,可以很方便的在一個線程空間里同時處理多個Web請求。
  • 方便進行各種負載均衡和請求轉發,不會造成後端Web應用阻塞。

  1. Web開發有3P:Perl、Python、PHP。Perl是1987年發布的,Python是1989年,PHP是1995年。CGI標準提出的時候正是Perl如日中天的時候,所以CGI的提出當時也是主要為了解決Perl作為Web編程語言的需求。熟悉正則(regex)的程序員可能知道正則的事實標準叫做pcre(Perl兼容正則表達式,Perl Compatible Regular Expressions),這也從一個側面體現了Perl作為一個古老的語言在當時對各種業界標準的影響。

From:漫談CGI FastCGI WSGI


這個問題可以分兩個層面討論:

1. PHP 解釋器是否嵌入 Web 伺服器進程內部執行

mod_php 通過嵌入 PHP 解釋器到 Apache 進程中,只能與 Apache 配合使用,而 cgi 和 fast-cgi 以獨立的進程的形式出現,只要對應的Web伺服器實現 cgi 或者 fast-cgi 協議,就能夠處理 PHP 請求。

mod_php 這種嵌入的方式最大的弊端就是內存佔用大,不論是否用到 PHP 解釋器都會將其載入到內存中,典型的就是處理CSS、JS之類的靜態文件是完全沒有必要載入解釋器。

2. 單個進程處理的請求數量

mod_php 和 fast-cgi 的模式在每個進程的生命周期內能夠處理多個請求,而 cgi 的模式處理一個請求就馬上銷毀進程,在高並發的場景下 cgi 的性能非常糟糕。

綜上,如果對性能有極高的要求,可以將靜態請求和動態請求分開,這時 Nginx + php-fpm 是比較好的選擇。

PS: cgi、fastcgi 通常指 Web 伺服器與解釋器通信的協議規範,而 php-fpm 是 fastcgi 協議的一個實現。


CGI 中文翻譯是通用網關介面,它是一種通信協議,讓腳本語言(比如PHP)具備和 HTTP Server 交互的能力。

FastCGI 是對 CGI 的一種改良,解決了 CGI 協議的一些性能問題,在 PHP 平台被廣泛採用。類似的東西還有 WSGI。

php-fpm 就是FastCGI 的一種實現,附帶了進程管理的功能。


CGI、FastCGI和PHP-FPM關係圖解:

  • CGI:是 Web Server 與 Web Application 之間數據交換的一種協議。
  • FastCGI:同 CGI,是一種通信協議,但比 CGI 在效率上做了一些優化。同樣,SCGI 協議與 FastCGI 類似。
  • PHP-CGI:是 PHP (Web Application)對 Web Server 提供的 CGI 協議的介面程序。
  • PHP-FPM:是 PHP(Web Application)對 Web Server 提供的 FastCGI 協議的介面程序,額外還提供了相對智能一些任務管理。


CGI看RFC3875:https://www.ietf.org/rfc/rfc3875

FastCGI看:FastCGI Specification

總的來說FastCGI是對CGI的性能改進,規範上有一些差異但是不大,與瀏覽器的通信規範是一致的。

PHP不了解。


這要了解萬維網 (WWW) 的歷史, 才能更好地了解個中關係.

早期的網站基本都是靜態的, 那時候的 web server 幾乎所有工作就是給訪問者提供靜態資源, 網站與訪問者之間缺乏交互. 後來隨著 WWW 的發展網站變得交互性強了起來, 交互性強了意味著 web server 端的業務邏輯複雜了起來, 不再是簡單地解析 url, 定位並返回用戶請求的資源, 而是要處理很多用戶請求的動態資源以及許多複雜的業務, 這些工作都交給 web server 來做是不現實的, 為何不現實呢說起來就羅嗦了, 以後再補充吧 (可以從網站的開發速度與難度, 可擴展性等方面考慮一下).

後來 CGI 出現了, 它使得 web server 可以把複雜的業務邏輯交給 cgi 腳本程序來做, CGI 協議定義了 web server 與 cgi 程序之間通信的 context, web server 一收到動態資源的請求就 fork 一個子進程調用 cgi 程序處理這個請求, 同時將和此請求相關的 context 傳給 cgi 程序, 像是 path_info, script path, request method, remote ip 等等...

顯然每次來個請求 web server 就去 fork 子進程是很低效的, 所以後來 fastcgi 出來了, 它定義了一種通信規範使得 cgi 程序和 web server 之間能通過 socket 通信, 這樣一來 cgi 這邊也需要有一個專門的 daemon 進程來和 web server 保持連接, php-fpm 就是這麼一個東西了.

對了, 你還說了個 apache 的 php5_module, 這是 apache 自己搞的一個插件架構, apache 自己有一套類似於 cgi 協議的東西, 好象是叫做 sapi, php 的處理程序是直接靜態或者動態編譯進 apache 的, 可以直接被 apache 通過函數調用來調用, 這個並不能算是所有的事都交給了 apache 去干, 這得益於 apache 的插件架構, 而這個插件架構背後的思想和 cgi/fastcgi 實際上已經沒有本質區別了

記得以前關於這個問題還寫了幾篇文章, 比上面說的詳細些, 有興趣可以看看, 一起討論

通用網關協議 (CGI) 進化史 下篇

通用網關協議 (CGI) 進化史 番外篇


前段時間面試的時候,被問答這個問題多次。看了上面的回答,覺著大部分都說到了重點,但始終差一點。今天讀php-internals,剛好看到了fastcgi這一部分,讀完之後發現講得真得透徹。我也不多說了,鏈接在這裡 第二章 ? 第二節 。裡面有詳細的例子,看了例子就會很清楚,希望能對大家有幫助。


什麼是CGI?

1.CGI,又叫通用網關介面;主要是HTTP伺服器和其他機器數據通信的工具。

2.一般Web Server接受瀏覽器的請求後,會判斷請求資源屬靜態資源(CSS,JS,JPG)還是動態資源(PHP);一般靜態資源直接返回給瀏覽器渲染即可,而PHP文件之類的動態資源就需要CGI進行處理了。

具體處理過程如下:

1.nginx收到index.html,index.php文件。

2.nginx進行內容分發,如果是index.html,nginx就在系統中找到這個文件,並發送給瀏覽器。如果是index.php則進行下面操作。

3.nginx發現是index.php,找到相關CGI程序,並提交相關參數(Http Header,GET/POST,COOKIE...),而此處的CGI就是PHP解析器,簡明來說就是CGI規定nginx以特定格式來訪問自身。

4.PHP解析器會解析php.ini文件並初始化php環境,然後處理請求,返回結果至nginx,nginx再返回給瀏覽器。

CGI的通用性

1.既然CGI是一種程序,自然需要用編程語言來寫。你可以用任何一種編程語言編寫CGI。

2.其中Perl語言是UNIX特屬語言,有強大的字元串處理能力,特別適合CGI的編寫。

什麼是FastCGI?

1.簡單來說FastCGI就是為了提高CGI的性能的,每當有PHP文件請求CGI,CGI每次都會進行環境的初始化,非常消耗性能,響應速度也非常慢;

2.DuangDuangDuang,FastCGI出現了,FastCGI會啟動一個master進程,msater主要負責環境的初始化,之後都不用進行環境初始化了,這樣每當有php文件請求的時候,只需要將請求傳遞給worker進程就行了。

FastCGI和PHP-FPM關係?

1.PHP-FPM是PHP特有的,而FastCGI是一種通用實現,可以實現JAVA的,PHP的,.NET的。

2.在PHP中,PHP-FPM只是FastCGI的管理者;代替FastCGI負責處理web請求。

上面內容來自我剛創建的博客哦。。歡迎參觀~~~~~ &


PHP 解釋器的執行,主要有三者模式,mod_php、CGI、FastCGI。

  • mode_php 是Apache 的一個模塊,把PHP 解釋器嵌入到Apache 進程中。
  • CGI 和FastCGI 分別是一種協議。Web Server 實現了CGI 或FastCGI 協議的相應的應用程序(以下簡稱CGI 或FastCGI),就可以啟動PHP 解釋器處理PHP 請求。它們都是以獨立進程的形式存在。
  • mode_php 和FastCGI 在 單個進程中可以處理多個請求,CGI 在單個進程中只能處理一個請求。

php-cgi 是一種CGI 協議的實現

  • php-cgi 其實就是PHP 解析器
  • 在CGI 模式時,當Web Server 收到 xx/index.php 請求時,會啟動php-cgi,php-cgi 會解析php.ini 文件,初始化環境,然後根據請求參數進行處理,再返回處理後的結果。(都是以CGI 協議規範來進行)
  • php-cgi 在每個請求時都會啟動一個進程,然後讀取php.ini 進行解析,可想而知效率相對比較低。
  • php-cgi 無法實現平滑重啟。修改php.ini 配置後,後面啟動的php-cgi 程序還是不會感知。

php-fpm 即FastCGI Process Management,是一種FastCGI 協議的實現

  • 當請求到來時,php-fpm 啟動並讀取php.ini 文件完成初始化環境,然後啟動一個master,再啟動多個worker。當請求過來時,master 會傳遞給一個worker,然後等待下一個請求。php-fpm 會動態配置worker 的數量。
  • 一個php-fpm 進程可以處理多個請求,會啟動多個php-cgi 程序。
  • php-fpm 可以實現平衡重啟。修改php.ini 後,當啟用新的worker 會使用新的配置。


CGI,FastCGI都是標準。PHP-FPM是實現。

拿手機通訊技術來類比的話:3G,4G是標準,移動4G卡是實現,當然你也可以用 聯通4G卡。


CGI,Common Gateway Interface,通用網關介面。將用戶的指令通過它遞交給後台進行處理。可以理解為一種通用的協議,能夠標準化用戶指令,方便後台程序進行後續的處理。

FastCGI根據協議將CGI進行包裝,與外部程序如nginx進行通信。一般擁有主從進程。FastCGI可以管理多種語言的CGI,比如題主提到的PHP,另外還有Python、Ruby等。可以理解為一種CGI的成熟模型。

而PHP-FPM是一個很好地實現了FastCGI的程序,後來被PHP收入官方項目中了。可以理解為FastCGI的管理器。穩定地管理FastCGI進程。


推薦閱讀:

PHP 面試問哪些問題可以比較準確的反映出應聘者的開發水平?
PHP高級程序員所要掌握的技能?
作為一名合格的PHP程序員,應該進行哪些技術儲備?
PHP自由職業者靠譜嗎?或者說是大學在校生,靠接一些項目來做賺錢養活自己。?
網上的PHP視頻版本比較老,是否適合現在(2017)自學?

TAG:PHP | LAMP | Nginx | LNMP | php-fpm |