從CGI到FastCGI到PHP-FPM

從CGI到FastCGI到PHP-FPM

背景

筆者在學習這幾個名詞的時候,也是被百度到的相關文章迷惑。涉及到的主要名詞包括php

1. CGI協議
2. CGI腳本
3. PHP-CGI
4. FastCGI協議
5. PHP-FPM

要真正理解這些名詞,若是咱們硬生生的解釋,也很難記住。咱們能夠從web服務器開發的歷程來看,看看他們爲何會出現,以及他們解決了什麼問題。html

早些年的簡單服務器

早些年的服務器很簡單,你訪問一個網站,好比www.helloworld.com,網站只返回你一個靜態HTML頁面。爲了簡單起見,咱們假設網站值返回hello標題,這樣流程能夠用下圖表示nginx

image

這時候server是採用C語言編寫的,基本上一個簡的C腳本就能夠了,把這個服務器腳本命名爲hello_server.c,代碼能夠到github上下載git

在Linux環境中使用gcc編譯hello_server.cgithub

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開發者想開發服務端程序,編寫更多的功能,發現本身的語言無從下手

CGI協議與CGI程序

什麼是CGI協議和CGI程序

既然Web服務器想作的專注,但又要支持Web的飛速發展,同時還想讓其餘語言開發者也能加入Web開發。因此,就出了一種方式,Web服務器和其餘領域的開發者擬定一條協議,在保證遵照協議的基礎上,剩下的能夠自由發揮。沒錯,這條協議就是CGI協議,協議的內容能夠查看,而實現這個協議的腳本叫作CGI程序。CGI協議規定了須要向CGI腳本設置的環境變量和一些其餘信息,CGI程序完成某一個功能,能夠用PHP,Python,Shell或者C語言編寫。瀏覽器

這樣的好處Web開發更加開放了,能夠用任何開發者擅長的語言開發Web程序。好比,我是一個PHP開發者,用PHP有不少現成的庫訪問第三方服務,入MySQL,Memcache等,可是這些第三方服務使用C語言接入仍是比較麻煩的。有了CGI協議,咱們的Web處理流程能夠變成下圖這樣服務器

image

實現一個PHP版的CGI程序

通常狀況下,咱們把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工做原理:每當客戶請求CGI的時候,WEB服務器就請求操做系統生成一個新的CGI解釋器進程,CGI進程則處理完一個請求後退出,下一個請求來時再建立新進程。咱們知道,執行一個PHP程序的必需要先解析php.ini文件,而後模塊初始化等等一系列工做,每次都反覆這樣很是浪費資源。固然,這樣在訪問量不多沒有併發的狀況也行。但是當訪問量增大,併發存在,這種方式就不適合了。這也是CGI程序一個致命的缺點。

FastCGI協議和php-cgi

FastCGI協議

既然上面提到了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程序的交互方式以下圖所示

image

PHP中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實現的已經很完美了,可是咱們的需求老是有點苛刻,致使它暴露了一下問題

  1. 當咱們更改配置文件後,php-cgi顯然沒法平滑重啓
  2. 咱們fork的進程個數和請求量正比,請求繁忙時,fork進程多,動態調整,顯然php-cgi沒作到

spawn-fcgi和PHP-FPM

既然上面說起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協議而且支持進程平滑重啓

相關文章
相關標籤/搜索