最早的Web服務(wù)器,可以簡單地響應(yīng)瀏覽器發(fā)來的HTTP請求,并將存儲在服務(wù)器上的HTML文件返回給瀏覽器,也就是靜態(tài)html。
隨著時間的變化,網(wǎng)站也越來越復雜,所以出現(xiàn)動態(tài)技術(shù)。但是服務(wù)器并不能直接運行 php,asp這樣的文件,自己不能做,外包給別人吧,但是要與第三做個約定,我給你什么,然后你給我什么,就是握把請求參數(shù)發(fā)送給你,然后我接收你的處 理結(jié)果給客戶端。那這個約定就是 common gateway interface,簡稱cgi。(cgi只是接口協(xié)議)

cgi就像翻譯機,將PHP語言給服務(wù)器解釋,便于相互之間的理解和通訊,最后呈現(xiàn)給瀏覽器查看

WEB服務(wù)器將根據(jù)CGI程序的類型決定數(shù)據(jù)向CGI程序的傳送方式,一般來講是通過標準輸入/輸出流和環(huán)境變量來與CGI程序間傳遞數(shù)據(jù)。 如下圖所示:

CGI程序通過標準輸入(STDIN)和標準輸出(STDOUT)來進行輸入輸出。此外CGI程序還通過環(huán)境變量來得到輸入,操作系統(tǒng)提供了許 多環(huán)境變量,它們定義了程序的執(zhí)行環(huán)境,應(yīng)用程序可以存取它們。
Web服務(wù)器和CGI接口又另外設(shè)置了一些環(huán)境變量,用來向CGI程序傳遞一些重要的參 數(shù)。CGI的GET方法還通過環(huán)境變量QUERY-STRING向CGI程序傳遞Form中的數(shù)據(jù)。 下面是一些常用的CGI環(huán)境變量:
| 變量名 | 描述 |
|---|---|
| CONTENT_TYPE | 這個環(huán)境變量的值指示所傳遞來的信息的MIME類型。目前,環(huán)境變量CONTENT_TYPE一般都是:application/x-www-form-urlencoded,他表示數(shù)據(jù)來自于HTML表單。 |
| CONTENT_LENGTH | 如果服務(wù)器與CGI程序信息的傳遞方式是POST,這個環(huán)境變量即使從標準輸入STDIN中可以讀到的有效數(shù)據(jù)的字節(jié)數(shù)。這個環(huán)境變量在讀取所輸入的數(shù)據(jù)時必須使用。 |
| HTTP_COOKIE | 客戶機內(nèi)的 COOKIE 內(nèi)容。 |
| HTTP_USER_AGENT | 提供包含了版本數(shù)或其他專有數(shù)據(jù)的客戶瀏覽器信息。 |
| PATH_INFO | 這個環(huán)境變量的值表示緊接在CGI程序名之后的其他路徑信息。它常常作為CGI程序的參數(shù)出現(xiàn)。 |
| QUERY_STRING | 如果服務(wù)器與CGI程序信息的傳遞方式是GET,這個環(huán)境變量的值即使所傳遞的信息。這個信息經(jīng)跟在CGI程序名的后面,兩者中間用一個問號'?'分隔。 |
| REMOTE_ADDR | 這個環(huán)境變量的值是發(fā)送請求的客戶機的IP地址,例如上面的192.168.1.67。這個值總是存在的。而且它是Web客戶機需要提供給Web服務(wù)器的唯一標識,可以在CGI程序中用它來區(qū)分不同的Web客戶機。 |
| REMOTE_HOST | 這個環(huán)境變量的值包含發(fā)送CGI請求的客戶機的主機名。如果不支持你想查詢,則無需定義此環(huán)境變量。 |
| REQUEST_METHOD | 提供腳本被調(diào)用的方法。對于使用 HTTP/1.0 協(xié)議的腳本,僅 GET 和 POST 有意義。 |
| SCRIPT_FILENAME | CGI腳本的完整路徑 |
| SCRIPT_NAME | CGI腳本的的名稱 |
| SERVER_NAME | 這是你的 WEB 服務(wù)器的主機名、別名或IP地址。 |
| SERVER_SOFTWARE | 這個環(huán)境變量的值包含了調(diào)用CGI程序的HTTP服務(wù)器的名稱和版本號。例如,上面的值為Apache/2.2.14(Unix) |
CGI
CGI全稱是 公共網(wǎng)關(guān)接口(Common Gateway Interface),HTTP服務(wù)器與你的或其它機器上的程序進行交談的一種工具,其程序須運行在網(wǎng)絡(luò)服務(wù)器上。
CGI可以用任何一種語言編寫,只要這種語言具有標準輸入、輸出和環(huán)境變量。如php,perl,tcl等。
CGI是HTTP Server和一個獨立的進程之間的協(xié)議,把HTTP Request的Header設(shè)置成進程的環(huán)境變量,HTTP Request的正文設(shè)置成進程的標準輸入,而進程的標準輸出就是HTTP Response包括Header和正文。
FastCGI
FastCGI像是一個常駐(long-live)型的CGI,它可以一直執(zhí)行著,只要激活后,不會每次都要花費時間去fork一次(這是CGI最為人詬病的fork-and-execute 模式)。它還支持分布式的運算,即 FastCGI 程序可以在網(wǎng)站服務(wù)器以外的主機上執(zhí)行并且接受來自其它網(wǎng)站服務(wù)器來的請求。
FastCGI是語言無關(guān)的、可伸縮架構(gòu)的CGI開放擴展,其主要行為是將CGI解釋器進程保持在內(nèi)存中并因此獲得較高的性能。眾所周知,CGI解釋器的反復加載是CGI性能低下的主要原因,如果CGI解釋器保持在內(nèi)存中并接受FastCGI進程管理器調(diào)度,則可以提供良好的性能、伸縮性、Fail- Over特性等等。
FASTCGI是和HTTP協(xié)議類似的概念。無非就是規(guī)定了在同一個TCP連接里怎么同時傳多個HTTP連接。這實際上導致了個問題,有個HTTP連接傳個大文件不肯讓出FASTCGI連接,在同一個FASTCGI連接里的其他HTTP連接就傻了。所以Lighttpd? 引入了 X-SENDFILE 。
FastCGI特點
FastCGI具有語言無關(guān)性.
FastCGI在進程中的應(yīng)用程序,獨立于核心web服務(wù)器運行,提供了一個比API更安全的環(huán)境。APIs把應(yīng)用程序的代碼與核心的web服務(wù)器鏈接在一起,這意味著在一個錯誤的API的應(yīng)用程序可能會損壞其他應(yīng)用程序或核心服務(wù)器。 惡意的API的應(yīng)用程序代碼甚至可以竊取另一個應(yīng)用程序或核心服務(wù)器的密鑰。
FastCGI技術(shù)目前支持語言有:C/C++、Java、Perl、Tcl、Python、SmallTalk、Ruby等。相關(guān)模塊在Apache, ISS, Lighttpd等流行的服務(wù)器上也是可用的。
FastCGI的不依賴于任何Web服務(wù)器的內(nèi)部架構(gòu),因此即使服務(wù)器技術(shù)的變化, FastCGI依然穩(wěn)定不變。
FastCGI的工作原理
Web Server啟動時載入FastCGI進程管理器(IIS ISAPI或Apache Module)
FastCGI進程管理器自身初始化,啟動多個CGI解釋器進程(可見多個php-cgi)并等待來自Web Server的連接。
當客戶端請求到達Web Server時,F(xiàn)astCGI進程管理器選擇并連接到一個CGI解釋器。Web server將CGI環(huán)境變量和標準輸入發(fā)送到FastCGI子進程php-cgi。
FastCGI子進程完成處理后將標準輸出和錯誤信息從同一連接返回Web Server。當FastCGI子進程關(guān)閉連接時,請求便告處理完成。FastCGI子進程接著等待并處理來自FastCGI進程管理器(運行在Web Server中)的下一個連接。 在CGI模式中,php-cgi在此便退出了。
在上述情況中,你可以想象CGI通常有多慢。每一個Web請求PHP都必須重新解析php.ini、重新載入全部擴展并重初始化全部數(shù)據(jù)結(jié)構(gòu)。使用FastCGI,所有這些都只在進程啟動時發(fā)生一次。一個額外的好處是,持續(xù)數(shù)據(jù)庫連接(Persistent database connection)可以工作。
FastCGI的不足
因為是多進程,所以比CGI多線程消耗更多的服務(wù)器內(nèi)存,PHP-CGI解釋器每進程消耗7至25兆內(nèi)存,將這個數(shù)字乘以50或100就是很大的內(nèi)存數(shù)。
fastcgi跟cgi的區(qū)別是:?
| 在web服務(wù)器方面 | 在對數(shù)據(jù)進行處理的進程方面 | |
|---|---|---|
| CGI | fork一個新的進程進行處理 | 讀取參數(shù),處理數(shù)據(jù),然后就結(jié)束生命期 |
| FAST-CGI | 用tcp方式跟遠程機子上的進程或本地進程建立連接 | 要開啟tcp端口,進入循環(huán),等待數(shù)據(jù)的到來,處理數(shù)據(jù) |
舉個例子: 服務(wù)端現(xiàn)在有個10萬個字單詞, 客戶每次會發(fā)來一個字符串,問以這個字符串為前綴的單詞有多少個。 那么可以寫一個程序,這個程序會建一棵trie樹,然后每次用戶請求過來時可以直接到這個trie去查找。 但是如果以cgi的方式的話,這次請求結(jié)束后這課trie也就沒了,等下次再啟動該進程時,又要新建一棵trie樹,這樣的效率就太低下了。 而用fastcgi的方式的話,這課trie樹在進程啟動時建立,以后就可以直接在trie樹上查詢指定的前綴了。
apache 模塊方式
記得曾在xp 配置 apache + php ,會在apache 配置下面一段:
LoadModule php5_module C:/php/php5apache2_2.dll
當PHP需要在Apache服務(wù)器下運行時,一般來說,它可以模塊的形式集成, 此時模塊的作用是接收Apache傳遞過來的PHP文件請求,并處理這些請求, 然后將處理后的結(jié)果返回給Apache。如果我們在Apache啟動前在其配置文件中配置好了PHP模塊, PHP模塊通過注冊apache2的ap_hook_post_config掛鉤,在Apache啟動的時候啟動此模塊以接受PHP文件的請求。
? Apache 的Hook機制是指:Apache 允許模塊(包括內(nèi)部模塊和外部模塊,例如mod_php5.so,mod_perl.so等)將自定義的函數(shù)注入到請求處理循環(huán)中。 換句話說,模塊可以在Apache的任何一個處理階段中掛接(Hook)上自己的處理函數(shù),從而參與Apache的請求處理過程。 mod_php5.so/ php5apache2.dll就是將所包含的自定義函數(shù),通過Hook機制注入到Apache中,在Apache處理流程的各個階段負責處理php請 求。
有人測試nginx+PHP-FPM在高并發(fā)情況下可能會達到Apache+mod_php5的5~10倍,現(xiàn)在nginx+PHP-FPM使用的人越來越多。
cgi 與 fastcgi
CGI工作原理:每當客戶請求CGI的時候,WEB服務(wù)器就請求操作系統(tǒng)生成一個新的CGI解釋器進程(如php-cgi.exe),CGI 的一個進程則處理完一個請求后退出,下一個請求來時再創(chuàng)建新進程。當然,這樣在訪問量很少沒有并發(fā)的情況也行??墒钱斣L問量增大,并發(fā)存在,這種方式就不 適合了。于是就有了fastcgi。
FastCGI像是一個常駐(long-live)型的CGI,它可以一直執(zhí)行著,只要激活后,不會每次都要花費時間去fork一次(這是CGI最為人詬病的fork-and-execute 模式)。
一般情況下,F(xiàn)astCGI的整個工作流程是這樣的:
1.Web Server啟動時載入FastCGI進程管理器(IIS ISAPI或Apache Module)
? 2.FastCGI進程管理器自身初始化,啟動多個CGI解釋器進程(可見多個php-cgi)并等待來自Web Server的連接。
? 3.當客戶端請求到達Web Server時,F(xiàn)astCGI進程管理器選擇并連接到一個CGI解釋器。 Web server將CGI環(huán)境變量和標準輸入發(fā)送到FastCGI子進程php-cgi。
? 4.FastCGI 子進程完成處理后將標準輸出和錯誤信息從同一連接返回Web Server。當FastCGI子進程關(guān)閉連接時, 請求便告處理完成。FastCGI子進程接著等待并處理來自FastCGI進程管理器(運行在Web Server中)的下一個連接。 在CGI模式中,php-cgi在此便退出了。
? PHP-FPM與Spawn-FCGI
Spawn-FCGI是一個通用的FastCGI管理服務(wù)器,它是lighttpd中的一部份,很多人都用Lighttpd的Spawn-FCGI進行FastCGI模式下的管理工作。 但是有缺點,于是PHP-fpm就是針對于PHP的,F(xiàn)astcgi的一種實現(xiàn),他負責管理一個進程池,來處理來自Web服務(wù)器的請求。目前,PHP-fpm是內(nèi)置于PHP的。
PHP-CGI
PHP-CGI是PHP自帶的FastCGI管理器。
PHP-CGI的不足:
php-cgi變更php.ini配置后需重啟php-cgi才能讓新的php-ini生效,不可以平滑重啟。
直接殺死php-cgi進程,php就不能運行了。(PHP-FPM和Spawn-FCGI就沒有這個問題,守護進程會平滑從新生成新的子進程。)
php-cgi是php提供給web serve也就是http前端服務(wù)器的cgi協(xié)議接口程序,當每次接到http前端服務(wù)器的請求都會開啟一個php-cgi進程進行處理,而且開啟的php-cgi的過程中會先要重載配置,數(shù)據(jù)結(jié)構(gòu)以及初始化運行環(huán)境,如果更新了php配置,那么就需要重啟php-cgi才能生效,例如phpstudy就是這種情況。
PHP-FPM
PHP-FPM是一個PHP FastCGI管理器,是只用于PHP的,可以在 http://php-fpm.org/download下載得到。
PHP-FPM其實是PHP源代碼的一個補丁,旨在將FastCGI進程管理整合進PHP包中。必須將它patch到你的PHP源代碼中,在編譯安裝PHP后才可以使用。
現(xiàn)在我們可以在最新的PHP 5.3.2的源碼樹里下載得到直接整合了PHP-FPM的分支,據(jù)說下個版本會融合進PHP的主分支去。相對Spawn-FCGI,PHP-FPM在CPU和內(nèi)存方面的控制都更勝一籌,而且前者很容易崩潰,必須用crontab進行監(jiān)控,而PHP-FPM則沒有這種煩惱。
PHP5.3.3已經(jīng)集成php-fpm了,不再是第三方的包了。PHP-FPM提供了更好的PHP進程管理方式,可以有效控制內(nèi)存和進程、可以平滑重載PHP配置,所以被PHP官方收錄了。
PHP-FPM的使用非常方便,配置都是在PHP-FPM.ini的文件內(nèi),而啟動、重啟都可以從php/sbin/PHP-FPM中進行。更方便的是修改php.ini后可以直接使用PHP-FPM reload進行加載,無需殺掉進程就可以完成php.ini的修改加載
結(jié)果顯示使用PHP-FPM可以使php有不小的性能提升。PHP-FPM控制的進程cpu回收的速度比較慢,內(nèi)存分配的很均勻。
而PHP-FPM合理的分配,導致總體響應(yīng)的提到以及任務(wù)的平均。
php-fpm是php提供給web serve也就是http前端服務(wù)器的fastcgi協(xié)議接口程序,它不會像php-cgi一樣每次連接都會重新開啟一個進程,處理完請求又關(guān)閉這個進程,而是允許一個進程對多個連接進行處理,而不會立即關(guān)閉這個進程,而是會接著處理下一個連接。它可以說是php-cgi的一個管理程序,是對php-cgi的改進。
php-fpm會開啟多個php-cgi程序,并且php-fpm常駐內(nèi)存,每次web serve服務(wù)器發(fā)送連接過來的時候,php-fpm將連接信息分配給下面其中的一個子程序php-cgi進行處理,處理完畢這個php-cgi并不會關(guān)閉,而是繼續(xù)等待下一個連接,這也是fast-cgi加速的原理,但是由于php-fpm是多進程的,而一個php-cgi基本消耗7-25M內(nèi)存,因此如果連接過多就會導致內(nèi)存消耗過大,引發(fā)一些問題,例如nginx里的502錯誤。
同時php-fpm還附帶一些其他的功能:
例如平滑過渡配置更改,普通的php-cgi在每次更改配置后,需要重新啟動才能初始化新的配置,而php-fpm是不需要,php-fpm分將新的連接發(fā)送給新的子程序php-cgi,這個時候加載的是新的配置,而原先正在運行的php-cgi還是使用的原先的配置,等到這個連接后下一次連接的時候會使用新的配置初始化,這就是平滑過渡。
使用場景
一般web服務(wù)器接受到瀏覽器的請求時,如果是靜態(tài)資源的話就直接將其返回給瀏覽器,如果是動態(tài)資源的話那就沒有現(xiàn)成的資源返回了,那這個時候cgi就出場了
cgi可以理解為一種協(xié)議or一類處理程序,就是動態(tài)去生成文件,從程序上來理解就是web服務(wù)器exec這樣一個進程,然后交給他一些輸入?yún)?shù),他就慢慢的處理完后把結(jié)果返回給web服務(wù)器,那從協(xié)議層面來說cgi協(xié)議就是規(guī)范了web服務(wù)器和cgi程序的一些輸入輸出參數(shù)的含義
所以可以有很多不同的cgi程序,別可以執(zhí)行php腳本的or可以執(zhí)行python腳本的,只要符合這類規(guī)范就能供web服務(wù)器調(diào)用,當然它的缺點就是每次都需要去啟動這個cgi程序,這會使得處理速度很慢
針對這種缺陷加以改進就成了fastcgi,同樣的他也可以理解為一種協(xié)議or一個程序,它跟cgi的不同就是不需要每次去exec,它會事先啟動起來,作為一個cgi的管理服務(wù)器存在,預(yù)先啟動一系列的子進程來等待處理,然后等待web服務(wù)器發(fā)過來的請求,一旦接受到請求就交由子進程處理,這樣由于不需要在接受到請求后啟動cgi,會快很多。
phpfpm是php對fastcgi的一種具體實現(xiàn),它的啟動后會創(chuàng)建多個cgi子進程,然后主進程負責管理子進程,同時它對外提供一個socket,那web服務(wù)器當要轉(zhuǎn)發(fā)一個動態(tài)請求時只需要按照fastcgi協(xié)議要求的格式將數(shù)據(jù)發(fā)往這個socket的就可以了,那phpfpm創(chuàng)建的子進程去爭搶這個socket連接,誰搶到了誰處理并將結(jié)果返回給web服務(wù)器,那phpfpm主進程干什么了?比方說其中一個子進程異常退出了怎么辦,那phpfpm會去監(jiān)控他一旦發(fā)現(xiàn)一個cgi子進程就會又啟動一個,還有其他諸多管理功能
phpfpm作為一個獨立的進程存在 通過socket與nginx建立連接,而mod_php 是作為一個模塊被加載進了apache服務(wù)器,同時他們兩作為cgi調(diào)度管理器,他們對其管理的方式也不一樣
通俗的可以把服務(wù)器看作餐廳,用戶請求看作來用餐的顧客,服務(wù)器處理請求看作解決顧客的就餐問題(響應(yīng)輸出一份飯)。
服務(wù)器上靜態(tài)資源看作已做好的飯,只要放到餐盒里就可以返回給顧客,動態(tài)資源需要廚房大廚現(xiàn)成做份再放到餐盒里返回給顧客。
php_mod這個大廚有個特點,看見有顧客進門就點火,不管顧客要不要現(xiàn)做的,有點浪費資源
php_fpm這個大廚有好多小弟一直點著火(多個處理進程),等有顧客說要現(xiàn)做,大廚就安排小弟做份返回給客戶
cgi也是個大廚,不過他等到顧客要現(xiàn)做,他才點火,做飯,然后熄火。等待下一個要現(xiàn)做的到來
fastcgi呢就是個大廚雇了一幫小弟,專門做需要現(xiàn)場做的飯,大廚只管分派任務(wù),小弟真正操鍋做飯。