在搭建 LAMP/LNMP 服務器時,會常常遇到 PHP-FPM、FastCGI和CGI 這幾個概念。若是對它們只知其一;不知其二,很難搭建出高性能的服務器。接下來咱們就以圖形方式,解釋這些概念之間的關係。html
在整個網站架構中,Web Server(如Apache)只是內容的分發者。舉個栗子,若是客戶端請求的是 index.html,那麼Web Server會去文件系統中找到這個文件,發送給瀏覽器,這裏分發的是靜態數據。mysql
若是請求的是 index.php,根據配置文件,Web Server知道這個不是靜態文件,須要去找 PHP 解析器來處理,那麼他會把這個請求簡單處理,而後交給PHP解析器。linux
當Web Server收到 index.php 這個請求後,會啓動對應的 CGI 程序,這裏就是PHP的解析器。接下來PHP解析器會解析php.ini文件,初始化執行環境,而後處理請求,再以規定CGI規定的格式返回處理後的結果,退出進程,Web server再把結果返回給瀏覽器。這就是一個完整的動態PHP Web訪問流程,接下來再引出這些概念,就好理解多了,nginx
WEB 中,web
在瞭解 CGI 以前,咱們先了解一下Web server 傳遞數據的另一種方法:PHP Module加載方式。以 Apache 爲例,在PHP Module方式中,是否是在 Apache 的配置文件 httpd.conf 中加上這樣幾句:sql
# 加入如下2句
LoadModule php5_module D:/php/php5apache2_2.dll
AddType application/x-httpd-php .php
# 修改以下內容
<IfModule dir_module>
DirectoryIndex index.php index.html
</IfModule>
上面是 Windows 下安裝php和apache環境後手動配置,在linux下源碼安裝大體是這樣配置的:數據庫
# ./configure --with-mysql=/usr/local --with-apache=/usr/local/apache --enable-track-vars
因此,這種方式,他們的共同本質都是用 LoadModule 來加載 php5_module,就是把php做爲apache的一個子模塊來運行。當經過web訪問php文件時,apache就會調用php5_module來解析php代碼。apache
那麼php5_module是怎麼來將數據傳給php解析器來解析php代碼的呢?答案是經過sapi。api
咱們再來看一張圖,詳細的說說apache 與 php 與 sapi的關係:
從上面圖中,咱們看出了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
好了。apache與php經過php5_module的方式就搞清楚了吧!
這種模式將php模塊安裝到apache中,因此每一次apache結束請求,都會產生一條進程,這個進程就完整的包括php的各類運算計算等操做。
在上圖中,咱們很清晰的能夠看到,apache每接收一個請求,都會產生一個進程來鏈接php經過sapi來完成請求,可想而知,若是一旦用戶過多,併發數過多,服務器就會承受不住了。
並且,把mod_php編進apache時,出問題時很難定位是php的問題仍是apache的問題。
CGI(Common Gateway Interface)全稱是「通用網關接口」,WEB 服務器與PHP應用進行「交談」的一種工具,其程序須運行在網絡服務器上。CGI能夠用任何一種語言編寫,只要這種語言具備標準輸入、輸出和環境變量。如php、perl、tcl等。
WEB服務器會傳哪些數據給PHP解析器呢?URL、查詢字符串、POST數據、HTTP header都會有。因此,CGI就是規定要傳哪些數據,以什麼樣的格式傳遞給後方處理這個請求的協議。仔細想一想,你在PHP代碼中使用的用戶從哪裏來的。
也就是說,CGI就是專門用來和 web 服務器打交道的。web服務器收到用戶請求,就會把請求提交給cgi程序(如php-cgi),cgi程序根據請求提交的參數做應處理(解析php),而後輸出標準的html語句,返回給web服服務器,WEB服務器再返回給客戶端,這就是普通cgi的工做原理。
CGI的好處就是徹底獨立於任何服務器,僅僅是作爲中間分子。提供接口給apache和php。他們經過cgi搭線來完成數據傳遞。這樣作的好處了儘可能減小2個的關聯,使他們2變得更獨立。
可是CGI有個蛋疼的地方,就是每一次web請求都會有啓動和退出過程,也就是最爲人詬病的fork-and-execute模式,這樣一在大規模併發下,就死翹翹了。
從根本上來講,FastCGI是用來提升CGI程序性能的。相似於CGI,FastCGI也能夠說是一種協議。
FastCGI像是一個常駐(long-live)型的CGI,它能夠一直執行着,只要激活後,不會每次都要花費時間去fork一次。它還支持分佈式的運算, 即 FastCGI 程序能夠在網站服務器之外的主機上執行,而且接受來自其它網站服務器來的請求。
FastCGI是語言無關的、可伸縮架構的CGI開放擴展,其主要行爲是將CGI解釋器進程保持在內存中,並所以得到較高的性能。衆所周知,CGI解釋器的反覆加載是CGI性能低下的主要緣由,若是CGI解釋器保持在內存中,並接受FastCGI進程管理器調度,則能夠提供良好的性能、伸縮性、Fail- Over特性等等。
FastCGI接口方式採用C/S結構,能夠將HTTP服務器和腳本解析服務器分開,同時在腳本解析服務器上啓動一個或者多個腳本解析守護進程。當HTTP服務器每次遇到動態程序時,能夠將其直接交付給FastCGI進程來執行,而後將獲得的結果返回給瀏覽器。這種方式可讓HTTP服務器專注地處理靜態請求,或者將動態腳本服務器的結果返回給客戶端,這在很大程度上提升了整個應用系統的性能。
FastCGI與CGI特色:
要了解PHP-FPM,就得先說說PHP-CGI。
PHP-CGI就是PHP實現的自帶的FastCGI管理器。 雖然是php官方出品,可是這丫的卻一點也不給力,性能太差,並且也很麻煩不人性化,主要體如今:
上面2個問題,一直讓不少人病垢了好久,因此不少人一直仍是在用 Module 方式。 直到 2004年一個叫 Andrei Nigmatulin的屌絲髮明瞭PHP-FPM ,這神器的出現就完全打破了這種局面,這是一個PHP專用的 fastcgi 管理器,它很爽的克服了上面2個問題,並且,還表如今其餘方面更表現強勁。
也就是說,PHP-FPM 是對於 FastCGI 協議的具體實現,他負責管理一個進程池,來處理來自Web服務器的請求。目前,PHP5.3版本以後,PHP-FPM是內置於PHP的。
由於PHP-CGI只是個CGI程序,他本身自己只能解析請求,返回結果,不會進程管理。因此就出現了一些可以調度 php-cgi 進程的程序,好比說由lighthttpd分離出來的spawn-fcgi。一樣,PHP-FPM也是用於調度管理PHP解析器php-cgi的管理程序。
PHP-FPM經過生成新的子進程能夠實現php.ini修改後的平滑重啓。
最後,咱們來總結一下,這些技術通過不斷的升級,能夠解決什麼問題(否則也不會升級嘛)。
因此,若是要搭建一個高性能的PHP WEB服務器,目前最佳的方式是Apache/Nginx + FastCGI + PHP-FPM(+PHP-CGI)方式了,不要再使用 Module加載或者 CGI 方式啦:)
本文章圖片用Visio製做,源文件:php-fpm