CGI、FastCGI 和 PHP-FPM

早期的網頁只是按照客戶端請求將保存在 web server 中的靜態資源回傳(例如圖片、CSS、HTML),這種情況下客戶端每次獲取的訊息都是同樣的內容,而現今僅僅通過靜態資源已經無法滿足客戶的需求,所以引入 CGI 以便客戶端請求能夠觸發 web server 運行另一個外部程式,連同客戶端所輸入的參數一起傳給這個外部程式,該程式會將動態生成的 HTML 和其他資訊通過 web server 再返回給客戶端(即動態請求,例如基於 PHP、Python、Java 實現的應用)。利用 CGI 可以針對用戶請求動態返回給客戶端各種各樣動態變化的訊息。

以 PHP 來說,PHP 的運作模式分成幾種:

  • CLI命令行模式
  • Module 模式
  • CGI 模式
  • Fast-CGI 模式

這篇只先討論下面兩種模式,有興趣暸解 Module 模式的可以看 PHP 的 Web 運行原理 ( 1 ) - 傳統型

CGI

CGI(Common Gateway Interface),為 web server 與 CGI 程式(如 php, python)間進行“交談”的一種工具或協議。CGI 程式可以用任何一種語言編寫,只要這種語言具有標準輸入、輸出和環境變量,如 php、perl、tcl 等。

根據使用者的請求,web server 會傳一些資訊給 PHP 解析器,例如 URL、查詢字串、POST 內容、HTTP header。CGI 就是規定要傳哪些資訊,以什麼樣的格式傳遞給後方處理這個請求的協議。web 服務器收到用戶請求,就會把請求提交給 CGI 程式(如php-cgi),CGI 程式根據請求的參數來解析程式,然後輸出標準的 html 語句,返回給 web 服務器, web 服務器再返回給客戶端,這就是 CGI 的工作流程,可以參考下圖。

common-gateway-interface-working
圖片來源

CGI program 針對每個 HTTP 請求都會 fork 一個新的 process 來做事,例如解析配置文件、初始化執行環境、處理請求等,然後把這個 process 處理完的結果通過 web server 轉發給用戶,剛剛 fork 出的 process 也隨之退出,如果下次用戶再請求動態資源,那麼 web server 又再次 fork process,如此周而復始,過程大概如下。

CGI program 的工作流程:

  1. 初始化各種相關變量
  2. 調用並初始化 zend 虛擬機
  3. 載入並解析 php.ini
    • 啟動 zend, zend 載入 php 腳本,做語法分析
    • 編譯 php 腳本成 opcode
    • 輸出結果
    • 關閉虛擬機
  4. 回傳結果給 web server

傳統 CGI 主要缺點是效能很差,因為每一次 web 請求都會有啟動和退出的過程,也就是 fork-and-execute 模式。這個模式幾乎無法處理高併發的請求,因此就誕生了 FastCGI。此外,傳統的 CGI 安全性也很差,現在已經很少被使用了。

FastCGI

FastCGI 是 CGI 的升級版本,為了提升 CGI 的效能而生,也是種協議。與 CGI 的 fork-and-execute 模式不同,FastCGI 則會先 fork 一個 master process,解析配置文件,初始化執行環境,然後再 fork 多個 worker process,當請求過來時 master process 會將請求分配給一個 worker process,然後立即可以接受下一個請求,提高了處理請求的效率,而且當 worker process 不夠用時,master process 還可以根據配置預先啟動幾個 worker process 等著,當閒置的 worker process 太多時,也會關掉一些,這樣不僅提高了效能,也節省了系統資源。另外常佇運行的 master process 同時也避免了重複的初始化操作。

FastCGI 採用 client-server 結構,可以將 web server 和腳本解析服務器分開,同時在腳本解析服務器上啟動一個或者多個腳本解析 process。當 web server 每次遇到動態程序時,可以將其直接交付給 FastCGI process 來執行,然後將得到的結果返回給瀏覽器。這種方式可以讓 web server 專注處理靜態請求,或者將動態腳本服務器的結果返回給客戶端,提高了整個應用系統的性能。

PHP-FPM

PHP-FPM (php-Fastcgi Process Manager) 是對於 FastCGI 協議的具體實現。

FastCGI 只是一個協議規範,需要每個語言具體去實現,PHP-FPM 就是 PHP 版本的 FastCGI 協議的具體實作,目的為實現 web server 與 PHP 腳本之間的溝通。

PHP-FPM 負責管理一個 process pool 來處理來自 web server 的 HTTP 動態請求,在 PHP-FPM 中,master process 負責與 Web server 進行通訊,接收 HTTP 請求,再將請求轉發給 worker process 進行處理,worker process 主要負責動態執行 PHP 程式碼,處理完成後,將處理結果返回給 web 服務器,再由 web 服務器將結果發送給客戶端。這就是PHP-FPM 的基本工作原理,概念可以看下圖:

總結

  • CGI: 介於 HTTP server 與 CGI program 溝通的工具、協定。
  • FastCGI: CGI 的改版,其 process 會持續運行,並支援分佈式運行。

  • PHP-CGI: 一種實作 FastCGI 的 process manager

  • PHP-FPM: 另一種實作 FastCGI 的 process manager,原本是 PHP 的補丁,在 PHP 5.3.2 被官方加入 PHP。

最後附上幾篇文章討論關於 PHP-FPM 的優化建議:

PHP Process Tuning

php-fpm详解 (這篇有討論關於 502 Bad Gateway & 504 Gateway Time-out 的優化建議)

Reference

通用閘道器介面

IT 考古: 什麼是 CGI?

CGI、FastCGI和PHP-FPM关系图解

What is Common Gateway Interface : Working and Its Applications

PHP-FPM是什么?

FastCGI Process Manager (FPM)

Differences between CGI, FastCGI, PHP-CGI, PHP-FPM, Spawn-FCGI

PHP 的 Web 運行原理 ( 1 ) - 傳統型

CGI、FastCGI、PHP-CGI與PHP-FPM

php-fpm與fastcgi、php-cgi之間的關係及源碼解析

【PHP核心】PHP的執行模式