在搭建 LAMP/LNMP 服務器時,會經常遇到 PHP-FPM、FastCGI和CGI 這幾個概念。如果對它們一知半解,很難搭建出高性能的服務器。
0.CGI的引入
在網站的整體架構中,Web Server(如nginx,apache)只是內容的分發者,對客戶端的請求進行應答。
如果客戶端請求的是index.html這類靜態頁面,那么Web Server就去文件系統中找對應的文件,找到返回給客戶端(一般是瀏覽器),在這里Web Server分發的就是是靜態數據。
整個過程如下圖:
對於像index.php這類的動態頁面請求,Web Server根據配置文件知道這個不是靜態文件,則會調用PHP 解析器進行處理然后將返回的數據轉發給客戶端(瀏覽器)。
整個過程如下圖:
在這個過程中,Web Server並不能直接處理靜態或者動態請求,對於靜態請求是直接查找然后返回數據或者報錯信息,對於動態數據也是交付給其他的工具(這里的PHP解析器)進行處理。
那么Web Server和處理工具(這里的php-fpm)是怎樣進行交互的呢?傳輸的是那些數據呢?這些數據的格式又是怎樣的呢?
由此便引出了今天的主角:CGI
1.關於CGI
1.1.什么是CGI?
1)CGI(Common Gateway Interface)全稱是“通用網關接口”,是一種讓客戶端(web瀏覽器)與Web服務器(nginx等)程序進行通信(數據傳輸)的協議。
用來規范web服務器傳輸到php解釋器中的數據類型以及數據格式,包括URL、查詢字符串、POST數據、HTTP header等,也就是為了保證web server傳遞過來的數據是標准格式的。
2)CGI可以用任何一種具有標准輸入、輸出和環境變量的語言編寫,如php、perl、tcl等。
不同類型語言寫的程序只要符合cgi標准,就能作為一個cgi程序與web服務器交互,早期的cgi大多都是c或c++編寫的。
3)一般說的CGI指的是用各種語言編寫的能實現該功能的程序。
1.2.CGI程序的工作原理
1)每次當web server收到index.php這種類型的動態請求后,會啟動對應的CGI程序(PHP的解析器);
2)PHP解析器會解析php.ini配置文件,初始化運行環境,然后處理請求,處理完成后將數據按照CGI規定的格式返回給web server然后退出進程;
3)最后web server再把結果返回給瀏覽器。
1.3.CGI程序的特點
1)高並發時的性能較差:
CGI程序的每一次web請求都會有啟動和退出過程,也就是最為人詬病的fork-and-execute模式(每次HTTP服務器遇到動態請求時都需要重新啟動腳本解析器來解析php.ini,重新載入全部DLL擴展並重初始化全部數據結構,然后把結果返回給HTTP服務器),很明顯,這樣的接口方式會導致php的性能很差,在處理高並發訪問時,幾乎是不可用的。
2)傳統的CGI接口方式安全性較差
3)CGI對php.ini的配置很敏感,在開發和調試的時候相當方便
1.4.CGI程序的應用領域
因為CGI為每一次請求增加一個進程,效率很低,所以基本已經不在生產部署時采用。但由於CGI對php配置的敏感性,通常被用在開發和調試階段
2.關於FastCGI
2.1.什么是FastCGI?
通過CGI程序的工作原理可以看出:CGI程序性能較差,安全性較低,為了解決這些問題產生了FastCGI。
1)FastCGI(Fast Common Gateway Interface)全稱是“快速通用網關接口”
是通用網關接口(CGI)的增強版本,由CGI發展改進而來,主要用來提高CGI程序性能,
類似於CGI,FastCGI也是一種讓交互程序與Web服務器通信的協議
2)FastCGI致力於減少網頁服務器與CGI程序之間互動的開銷,從而使服務器可以同時處理更多的網頁請求(提高並發訪問)。
3)同樣的,一般說的FastCGI指的也是用各種語言編寫的能實現該功能的程序。
2.2.FastCGI程序的工作原理
1)Web Server啟動同時,加載FastCGI進程管理器(nginx的php-fpm或者IIS的ISAPI或Apache的Module)
2)FastCGI進程管理器讀取php.ini配置文件,對自身進行初始化,啟動多個CGI解釋器進程(php-cgi),等待來自Web Server的連接。
3)當Web Server接收到客戶端請求時,FastCGI進程管理器選擇並連接到一個CGI解釋器。Web server會將相關環境變量和標准輸入發送到FastCGI子進程php-cgi進行處理
4)FastCGI子進程完成處理后將數據按照CGI規定的格式返回給Web Server,然后關閉FastCGI子進程或者等待下一次請求。
2.3.FastCGI對進程的管理方式
Fastcgi會先啟一個master,解析配置文件,初始化執行環境,然后再啟動多個worker。當請求過來時,master會傳遞給一個worker,然后立即可以接受下一個請求。這樣就避免了重復的勞動,效率自然提高。而且當worker不夠用時,master可以根據配置預先啟動幾個worker等着;當然空閑worker太多時,也會停掉一些,這樣就提高了性能,也節約了資源。這就是fastcgi的對進程的管理。
2.4.FastCGI的特點:
1)FastCGI具有語言無關性,支持用大多數語言進行編寫,對應的程序也支持大多數主流的web服務器
FastCGI技術目前支持語言有:C/C++,Java,PHP,Perl,Tcl,Python,SmallTalk,Ruby等。
支持FastCGI技術的主流web服務器有:Apache,Nginx,lighttpd等
2)FastCGI程序的接口方式采用C/S結構,可以將web服務器和腳本解析服務器分開,獨立於web服務器運行,提高web服務器的並發性能和安全性:
提高性能:這種方式支持多個web分發服務器和多個腳本解析服務器的分布式架構,同時可以在腳本解析服務器上啟動一個或者多個腳本解析守護進程來處理動態請求,可以讓web服務器專一地處理靜態請求或者將動態腳本服務器的結果返回給客戶端,這在很大程度上提高了整個應用系統的性能。
提高安全性:API方式把應用程序的代碼與核心的web服務器鏈接在一起,這時一個錯誤的API的應用程序可能會損壞其他應用程序或核心服務器,惡意的API的應用程序代碼甚至可以竊取另一個應用程序或核心服務器的密鑰,采用這種方式可以在很大程度上避免這個問題
3)FastCGI的不依賴於任何Web服務器的內部架構,因此即使服務器技術的變化, FastCGI依然穩定不變
4)FastCGI程序在修改php.ini配置時可以進行平滑重啟加載新配置
所有的配置加載都只在FastCGI進程啟動時發生一次,每次修改php.ini配置文件,只需要重啟FastCGI程序(php-fpm等)即可完成平滑加載新配置,已有的動態請求會繼續處理,處理完成關閉進程,新來的請求使用新加載的配置和變量進行處理
5)FAST-CGI是較新的標准,架構上和CGI大為不同,是用一個駐留內存的服務進程向網站服務器提供腳本服務。像是一個常駐(long-live)型的CGI,維護的是一個進程池,它可以一直執行着,只要激活后,不會每次都要花費時間去fork一次(這是CGI最為人詬病的fork-and-execute 模式),速度和效率比CGI大為提高,是目前的主流部署方式。
6)FastCGI的不足:
因為是在內存中同時運行多進程,所以會比CGI方式消耗更多的服務器內存,每個PHP-CGI進程消耗7至25兆內存,在進行優化配置php-cgi進程池的數量時要注意系統內存,防止過量
2.5.FastCGI程序的應用領域
生產環境的主流部署方式
2.6.關於CGI和FastCGI的總結
1)CGI 和 FastCGI 都只是一種通信協議規范,不是一個實體,一般說的CGI指的是用各種語言編寫的能實現該功能的程序
2)CGI 程序和FastCGI程序,是指實現這兩個協議的程序,可以是任何語言實現這個協議的。(PHP-CGI 和 PHP-FPM就是實現FastCGI的程序)
3)CGI程序和FastCGI程序的區別:
關於CGI程序:
CGI使外部程序與Web服務器之間交互成為可能。CGI程序運行在獨立的進程中,並對每個Web請求建立一個進程,這種方法非常容易實現,但效率很差,難以擴展。面對大量請求,進程的大量建立和消亡使操作系統性能大大下降。此外,由於地址空間無法共享,也限制了資源重用。
關於FastCGI程序:
與CGI程序為每個請求創建一個新的進程不同,FastCGI使用持續的進程(master)來處理一連串的請求。這些進程由FastCGI服務器管理,而不是web服務器。 當進來一個請求時,web服務器把環境變量和這個頁面請求通過一個socket或者一個TCP connection傳遞給FastCGI進程。
3.關於PHP-CGI,PHP-FPM和Spawn-FCGI
3.1.PHP-CGI是什么?
很多地方說:PHP-CGI是PHP自帶的FastCGI管理器,目前還沒找到最原始的出處,以我的理解和經驗來看這話有點毛病,我認為應該是:使用php實現CGI協議的CGI程序,可以用來管理php解釋器,如果有異議可以和我探討下。。。
使用如下命令可以啟動PHP-CGI:
php-cgi -b 127.0.0.1:9000
php-cgi的特點:
1)php-cgi變更php.ini配置后需重啟php-cgi才能讓新的配置生效,不可以平滑重啟
2)直接殺死php-cgi進程php就不能運行了
3.2.關於php-fpm
PHP-FPM(FastCGI Process Manager)
針對PHP-CGI的不足,PHP-FPM和Spawn-FCGI應運而生,它們的守護進程會平滑從新生成新的子進程。
1)PHP-FPM使用PHP編寫的PHP-FastCGI管理器,管理對象是PHP-CGI程序,不能說php-fpm是fastcgi進程的管理器,因為前面說了fastcgi是個協議
下載地址:http://php-fpm.org/download
早期的PHP-FPM是作為PHP源碼的補丁而使用的,在PHP的5.3.2版本中直接整合到了PHP-FPM分支,目前主流的PHP5.5,PHP5.6,PHP5.7已經集成了該功能(被官方收錄)
在配置時使用--enable-fpm參數即可開啟PHP-FPM
2)修改php.ini之后,php-cgi進程的確是沒辦法平滑重啟的。php-fpm對此的處理機制是新的worker用新的配置,已經存在的worker處理完手上的活就可以歇着了,通過這種機制來平滑過度。
3.3.關於Spawn-FCGI
1)Spawn-FCGI是一個通用的FastCGI管理服務器,它是lighttpd中的一部份,很多人都用Lighttpd的Spawn-FCGI進行FastCGI模式下的管理工作
2)Spawn-FCGI目前已經獨成為一個項目,更加穩定一些,也給很多Web 站點的配置帶來便利。已經有不少站點將它與nginx搭配來解決動態網頁。
Spawn-FCGI的下載地址是http://redmine.lighttpd.net/projects/spawn-fcgi,目前(20190114)最新版本為1.6.4,在4年前更新的,有點涼涼的意思。。。
3.4.PHP-FPM與spawn-CGI對比
1)PHP-FPM的配置都是在php-fpm.ini的文件內,早些時候重啟可以通過/usr/local/php/sbin/php-fpm reload進行,無需殺掉進程就可以完成php.ini的修改加載,被php官方收錄以后需要指定參數進行重載配置,可以自己創建腳本進行管理
2)PHP-FPM控制的進程cpu回收的速度比較慢,內存分配的很均勻,比Spawn-FCGI要好,可以有效控制內存和進程,且不容易崩潰,很優秀
3)Spawn-FCGI控制的進程CPU下降的很快,而內存分配的比較不均勻。有很多進程似乎未分配到,而另外一些卻占用很高。可能是由於進程任務分配的不均勻導致的。而這也導致了總體響應速度的下降。而PHP-FPM合理的分配,導致總體響應的提到以及任務的平均。
(摘錄的,暫未實際驗證)
4.PHP運行的5種模式
php目前比較常見的五大運行模式:包括cli、cgi 、fast-cgi、isapi、apache模塊的DLL
4.1.cli模式
cli模式就是php的命令行運行模式,
例如:在linux下經常使用 “php -m”查找PHP安裝了那些擴展就是PHP命令行運行模式
其他的可以輸入php -h查看下
4.2.CGI模式
比較經典的使用方法,使用CGI程序將瀏覽器,web服務器,php解釋器連接起來進行數據交換的工具,目前主要用來做開發或調試
CGI方式在遇到連接請求(用戶 請求)先要創建cgi的子進程,激活一個CGI進程,然后處理請求,處理完后結束這個子進程。這就是fork-and-execute模式。所以用cgi方式的服務器有多少連接請求就會有多少cgi子進程,子進程反復加載是cgi性能低下的主要原因。都會當用戶請求數量非常多時,會大量擠占系統的資源如內 存,CPU時間等,造成效能低下。
4.3.FastCGI模式
目前主流的使用方式,比CGI模式的工具效率高很多,大量用於分布式高並發的環境中
在Linux中,nginx加php-fpm是最主流的使用方式
4.4.ISAPI運行模式
ISAPI即Internet Server Application Program Interface,是微軟提供的一套面向Internet服務的API接口,一個ISAPI的DLL,可以在被用戶請求激活后長駐內存,等待用戶的另一個請求,還可以在一個DLL里設置多個用戶請求處理函數,此外,ISAPI的DLL應用程序和WWW服務器處於同一個進程中,效率要顯著高於CGI。(由於微軟的排他性,只能運行於windows環境)
4.5.apache模塊運行模式
此運行模式可以在Linux和windows環境下使用Apache,他們的共同點都是用 LoadModule 來加載相關模塊,有兩種類型
4.5.1.mod_php模塊
Apache調用php的相關模塊(php5_module),也就是把php作為apache的一個子模塊來運行
當通過web訪問php文件時,apache就會調用php5_module通過sapi將數據傳給php解析器來解析php代碼,整個過程如下圖:
從上面圖中,可以看出:
1)sapi就是這樣的一個中間過程,SAPI提供了一個和外部通信的接口,有點類似於socket,使得PHP可以和其他應用進行交互數據(apache,nginx等)。
php默認提供了很多種SAPI,常見的提供給apache和nginx的php5_module、CGI、FastCGI,給IIS的ISAPI,以及Shell的CLI。
所以,以上的apache調用php執行的過程如下:
apache -> httpd -> php5_module -> sapi -> php
2)apache每接收一個請求,都會產生一個進程來連接php通過sapi來完成請求,如果用戶過多,並發數過多,服務器就會承受不住了。
3)把mod_php編進apache時,出問題時很難定位是php的問題還是apache的問題,而且PHP是與Web服務器一起啟動並運行的,當php模塊出現問題可能會導致Apache一同掛掉
4.5.2.mod_cgi模塊
在此種模式中Apache啟動加載mod_cgi模塊,使用CGI調用管理動態的php請求
更高級的是mod_fcgid模塊,是apache的fastcgi實現,性能提高,在apache的2.4以后的版本中得到支持。
總結:
1)mod_php是apache的內置php解釋模塊,使用prefork方式,不需要額外的進程來做通訊和應用解釋,顯然mod_php比mod_cgi這樣方式性能要好得多
2)缺點是把應用和HTTP服務器綁定在了一起,當php模塊出現問題可能會導致Apache一同掛掉
3)另外每個Apache進程都需要加載mod_php而不論這個請求是處理靜態內容還是動態內容,這樣導致浪費內存,效率下降,
4)php.ini文件的變更需要重新啟動apache服務器才能生效,這使得無法進行平滑配置變更。
4.6.總結一下
隨着技術的不斷升級,單純的Apache加php模塊的方式已不再主流,而是替換為Apache加php_cgi,以及后來的php_fcgi和nginx加php-fpm的方法,可以看下圖:
5.參考文章:
CGI、FastCGI和PHP-FPM關系圖解:https://www.awaimai.com/371.html
關於CGI 和 PHP-FPM需要弄清的:http://www.cleey.com/blog/single/id/848.html
淺談PHP fastcgi和php-fpm:https://www.jianshu.com/p/d095cbcbcf1b
php五大運行模式CGI,FAST-CGI,CLI,ISAPI,APACHE模式淺談:https://www.cnblogs.com/XACOOL/p/5619650.html
==== 完畢,呵呵呵呵 ====