筆者在學習這幾個名詞的時候,也是被百度到的相關文章迷惑。涉及到的主要名詞包括php
1. CGI協議 2. CGI腳本 3. PHP-CGI 4. FastCGI協議 5. PHP-FPM
要真正理解這些名詞,若是咱們硬生生的解釋,也很難記住。咱們能夠從web服務器開發的歷程來看,看看他們爲何會出現,以及他們解決了什麼問題。html
早些年的服務器很簡單,你訪問一個網站,好比www.helloworld.com
,網站只返回你一個靜態HTML頁面。爲了簡單起見,咱們假設網站值返回hello標題,這樣流程能夠用下圖表示nginx
這時候server是採用C語言編寫的,基本上一個簡的C腳本就能夠了,把這個服務器腳本命名爲hello_server.c
,代碼能夠到github上下載git
在Linux環境中使用gcc編譯hello_server.c
github
vagrant@kfz:~$ gcc hello_world.c
而後採用curl
工具測試web
vagrant@kfz:~$ curl "127.0.0.1:8887" GET / HTTP/1.1 User-Agent: curl/7.49.1 Host: 127.0.0.1:8887 Accept: */* Hello World
這裏咱們的重點不在HTTP響應的格式上,因此咱們直接輸出了Hello World
,若是是請求靜態頁面,咱們也是同樣的思路,讀取靜態文件的內容而後返還給客戶端。shell
思考:當Web時代愈來愈火爆,咱們但願Web服務器有更多的功能,好比寫博客,聊天,等等。同時,愈來愈多的不一樣領域的開發者想在web時代大顯身手。不少服務器開發者發現有了如下缺點api
#服務器功能方面 Web服務器功能會隨着這種邏輯的增加而增加,服務器會變的不專注 #語言支持方便 愈來愈多的PHP,Python開發者想開發服務端程序,編寫更多的功能,發現本身的語言無從下手
既然Web服務器想作的專注,但又要支持Web的飛速發展,同時還想讓其餘語言開發者也能加入Web開發。因此,就出了一種方式,Web服務器和其餘領域的開發者擬定一條協議,在保證遵照協議的基礎上,剩下的能夠自由發揮。沒錯,這條協議就是CGI協議
,協議的內容能夠查看,而實現這個協議的腳本叫作CGI程序
。CGI協議規定了須要向CGI腳本設置的環境變量和一些其餘信息,CGI程序完成某一個功能,能夠用PHP,Python,Shell或者C語言編寫。瀏覽器
這樣的好處Web開發更加開放了,能夠用任何開發者擅長的語言開發Web程序。好比,我是一個PHP開發者,用PHP有不少現成的庫訪問第三方服務,入MySQL,Memcache等,可是這些第三方服務使用C語言接入仍是比較麻煩的。有了CGI協議,咱們的Web處理流程能夠變成下圖這樣服務器
通常狀況下,咱們把CGI程序放在cgi-bin目錄下面,新建PHP版CGI處理程序cgi-bin/get_user.php
,咱們的服務器程序仍是使用簡單的hello_server.c
。
目標:用戶訪問URL127.0.0.1:8887/cgi-bin/get_user.php?id=1
的的時候輸出用戶ID爲1的用戶信息
實現:很顯然,咱們要解決兩個問題
1. 在服務器程序hello_server.c中執行get_user.php文件 2. 服務器程序hello_server.c向get_user.php中傳遞請求信息(id=1)
第一個問題咱們使用C庫仍是popen
來運行php腳本,第二個問題在CGI協議中規定了咱們的解決方案。首先須要將請求參數放在環境變量中,其次,環境變量的名字也規定好了。這裏列舉一些CGI協議規定的經常使用的環境變量,這裏只列舉一些,所有的內容能夠協議明細
變量名 | 描述 |
---|---|
CONTENT_TYPE | 這個環境變量的值指示所傳遞來的信息的MIME類型。目前,環境變量CONTENT_TYPE通常都是:application/x-www-form-urlencoded,他表示數據來自於HTML表單 |
HTTP_USER_AGENT | 提供包含了版本數或其餘專有數據的客戶瀏覽器信息 |
QUERY_STRING | 若是服務器與CGI程序信息的傳遞方式是GET,這個環境變量的值即便所傳遞的信息。這個信息經跟在CGI程序名的後面,二者中間用一個問號'?'分隔 |
REQUEST_METHOD | 提供腳本被調用的方法。對於使用 HTTP/1.0 協議的腳本,僅 GET 和 POST 有意義 |
因此,關於服務端怎麼向CGI程序傳遞請求信息的問題就能夠解釋了。在服務端接收到用戶請求的時候須要按照CGI協議經過putenv
設置環境變量,而後PHP程序也是按照CGI協議解析環境變量(PHP中使用$_SERVER便可)。
程序的實現,能夠在這裏下載
最後須要說明的,當服務器執行php cgi-bin/get_user.php
的時候,其實是使用php的命令行模式執行的,由於PHP已經爲咱們封裝好了環境變量,因此咱們只須要從$_SERVER
中就能夠獲取咱們須要的環境變量。這裏只爲了說明問題,實際上PHP的sapi裏面有單獨實現cgi接口。固然你也能夠用shell實現,shell的話是直接獲取環境變量
CGI工做原理:每當客戶請求CGI的時候,WEB服務器就請求操做系統生成一個新的CGI解釋器進程,CGI進程則處理完一個請求後退出,下一個請求來時再建立新進程。咱們知道,執行一個PHP程序的必需要先解析php.ini文件,而後模塊初始化等等一系列工做,每次都反覆這樣很是浪費資源。固然,這樣在訪問量不多沒有併發的狀況也行。但是當訪問量增大,併發存在,這種方式就不適合了。這也是CGI程序一個致命的缺點。
既然上面提到了CGI程序的一個致命缺陷,因此在CGI協議的基礎上,又設計了FASTCGI協議
,協議的內容能夠查看,歸納一下,主要作了如下改變
1. FastCGI被設計用來支持常駐(long-lived)應用進程,減小了fork-and-execute帶來的開銷 2. FastCGI進程經過監聽的socket,收來自Web服務器的鏈接,這樣FastCGI進程能夠獨立部署 3. 服務器和FastCGI監聽的socket之間按照消息的形式發送環境變量和其餘數據
因此,咱們稱實現了FastCGI協議
的程序爲FastCGI程序
,在php中的sapi/cgi/php-cgi
實現了這個協議,雖然它的名字取名爲php-cgi,但實際上是支持FacsCGI協議的。因此,實現了FastCGI協議的服務器和FastCGI程序的交互方式以下圖所示
可能不多人用過php-cgi,由於目前你們都使用php-fpm(先不討論php-fpm)。這裏介紹一下php-cgi的使用。
1.php-cgi如何啓動監聽socket
php-cgi可執行程序爲編譯後的php目錄下面的sapi/cgi/php-cgi
。經過-b
參數指定須要監聽的ip和端口便可,好比咱們想綁定本機的9004端口
shell> ./php-cgi -b 127.0.0.1:9004
2.如何啓動多個FastCGI進程
這裏須要使用PHP_FCGI_CHILDREN
環境變量表示派生多少個子進程,咱們能夠寫一個shell來啓動php-cgi,點擊能夠下載這個shell文件。注意替換成本身的cgi路徑後再使用
3.經過shell腳本啓動FastCGI管理器
執行start.sh
腳本以後,能夠查看到啓動了多個FastCGI進程
shell> ps aux | grep php-cgi | grep -v grep vagrant 6785 0.0 0.0 1936 524 pts/3 S 01:07 0:00 sh -c /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004 vagrant 6792 0.0 0.3 13760 3188 ? Ss 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004 vagrant 6793 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004 vagrant 6794 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004 vagrant 6795 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004 vagrant 6796 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004 vagrant 6797 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004 vagrant 6798 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004 vagrant 6799 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004 vagrant 6800 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004
4.和Nginx配合測試
在Nginx配置文件中加上這一段
location ~ \.php$ { fastcgi_pass 127.0.0.1:9004; include fastcgi.conf; }
重啓nginx以後就能夠測試nginx訪問php了。
php-cgi實現的已經很完美了,可是咱們的需求老是有點苛刻,致使它暴露了一下問題
既然上面說起php-cgi實現的FastCGI問題官方沒有解決。那天然有第三方作這個事情,咱們可使用Lighttpd的spawn-fcgi來管理php-cgi進程。注意,這裏的spawn-fcgi是進程管理器,只是管理實現FastCGI協議的php-cgi,這個的安裝和使用方式能夠參照鳥哥的博客
還有一個作的比較好的,php-fpm,之前php-fpm並無歸入php安裝包文件中,只是在它的卓越表現,獲得了愈來愈多的人承認,後面就被歸入了官方默認安裝包文件中。php-fpm是從5.3開始才進入PHP源代碼主幹的,能夠獨立運行,不依賴php-cgi,換句話說,他本身實現了FastCGI協議。
因此,spawn-fcgi和PHP-FPM不是同一個東西,前者是進程管理器,後者卻實現FastCGI協議而且支持進程平滑重啓