PHP 的 Web 運行原理 ( 1 ) - 傳統型
php
Lastmod: 2019-12-15

這篇文章雖然主題為PHP 的 Web 運行原理,但是比較白話文的說,事實上是想要理解這件事情 :

一個 http 請求進來後,php 到底是如何運行呢 ?

要理解這件事情,有個最基本的觀念要先理解,那就是下面這段指令,它到底是如何運行的。

php index.php

然後接下來才能在理解 Web 是如何用 php 來處理。

這篇文章將分為以下幾個章節 :

  • 執行 php index.php 它是如何運行的呢 ?
  • 三種用 PHP 來處理 HTTP 的模式
  • Web PHP 應用組合與問題

執行 php index.php 它是如何運行的呢 ?

假設我們在 Terminal 執行了如下的指令。

php index.php

那實際上它的運行流程會如下圖,而這張圖也代表 PHP 的基本運行架構。

  • SAPI ( Server Application Programming Interface ) : 它就是一個應用環境PHP 核心的一個 Interface,會有這層主要的原因在於,不同的應用環境,例如命令行環境(就是在 Terminal 執行 php) 或 Web 環境都需要不同的 PHP 環境配置,如果沒有這一層就代表 PHP 本身要針對不同的環境來考慮設計兼容,這也是為什麼會有 SAPI 的目的。
  • main : 它是 php 所有操作的整合者。
  • Zend 引擎 : 它就是將咱們編寫的 PHP 程式碼解釋成可以執行的 opcode 碼,其中 PHP7 與 PHP5 有速度上的飛升原因就在於此,PHP7 大幅度的優化了 Zend 引擎。
  • Extension : 它是 PHP 內核所提供的一套擴充 PHP 功能的方式,大部份都是使用 C/C++ 所撰寫,基本上可以分為 PHP extension 與 Zend extension。

三種用 PHP 來處理 HTTP 的模式

那如果改成 Web 情況下,上面那張圖會變成什麼樣子呢 ? 如下圖,就只是將 Terminal 那改成 Web Server。

然後 Web 模式基本上可分為三種模式 :

  1. 模組模式
  2. CGI 模式 (這只是過渡期,現在沒啥在用)
  3. Fast-CGI 模式

Module 模式

在早期開發 PHP 時,大部份的人應該都用過 LAMP 這東西,它就是 Linux + Apache + MySQL + PHP 這些東組合。

然後在通常會在 Apache Web Server 上設置一段指令 :

LoadModule php5(or 7)_module modules/mod_php5.so

這一段就是讓 Apache 把 php 當成一個模塊來處理。

在種情況下,一個 http 請求它是如何的運行呢 ?

運行模式下圖 :

  1. 瀏覽器發送一個 http 請求到 Apache Web Server。
  2. Apache 收到以後,如果發現是 .php 的請求,則會使用 php5_module 來解析 php。
  3. php5_module 接下來會將 php 代碼丟到 sapi 來進行一些針對 Apache 的環境設定 然後最後在由 zedn 來處理這段程式碼。

但這裡有個問題。

那假如我不用 Apache 呢 ?

這就是為什麼會有下面這個模式的原因。

CGI 模式

雖然 LAMP 方案在當時很流行,但是並不是每個人都想使用 Apache,而且比較正確的說法,應該是說,不是所有的 Web Server 都有建立 PHP 模組,那這種情況下要怎麼辦呢 ?

這時就有所謂的CGI 協議

CGI 定義了讓 Web Server 與請求處理程序(ex. php)溝通的標準。

它的運行流程如下圖 :

  1. 瀏覽器發送一個 http 請求到 Web Server
  2. Web Server 產生出一個已實作 CGI 協議的 child process (ex. php-cgi)
  3. 子進程解析 php.ini 進行初使化環境。
  4. 處理 http 請求,並返回給 Web Server。
  5. 關閉子進程。

這裡有幾點事情要注意一下

首先實作 CGI 協議的 child process,這裡,這句話事實上也代表了不限制任何語言,而 php-cgi 就只是一個使用 php 來實作 CGI 協議的程式,它可以用來解析從 Web Server 傳送過來的 CGI 請求,並且用 php 來處理。

這種模式有什麼缺點呢 ?

上面使用這種 CGI 模式的缺點就在於,它每一個 http 請求就需要 fork 一個 CGI 子進程來處理,然後大量的請求一定會倒。

這也是為什麼之後會出現一個叫Fast-CGI的協議。

Fast-CGI

Fast-CGI它也是個協定,它運作的標準如下 :

  1. 先產生一個 master 進程。
  2. 解析 php.ini 來初使化環境。
  3. 然後預先建立多個 worker 進程。
  4. 當有一個請求時會分配給某個 worker 來處理。
  5. 當 worker 不夠用時,master 會在建立 worker,而當 worker 閒置太多時,master 也會減少 worker。

而這裡就要提到一個新名詞PHP-FPM,它就是根據 Fast-CGI 協議所實作進程管理器。

所以當一個 http 請求進來時,使際上的運作會如下圖 :

  1. 一個 http 請求從瀏覽器送到 Web Server。
  2. Web Server 會將此請求包含旁 Fast-CGI 協議標準,發送給 PHP-FPM 所管理的 Master 進程。
  3. 接下來 Master 進程會將請求在發送給某個 worker 來處理。
  4. 最後 worker 處理完後返回結果,然後在等待接客。

Web PHP 應用組合與問題

根據咱們上述學的模式,基本上現今 PHP Web 的組合大至上可以分為以下兩種 :

  1. Apache + php module
  2. Nginx + php-fpm

在早期的時後,基本上一定是選擇 Nginx + php-fpm 這種類型來開發 PHP Web 應用,主要的原因在於使用 moduel 模式的 PHP Web 它就只是單線程的應用,這也代表一定會有效能壓力,但是之後 Apache 提供了一個叫 MPM (Multi-Processing Module) 就解決了這個問題。

或需有人認為 Nginx 一定比較快,因為它有 Event Loop 機制,可以不塞車,但是問題是處理 PHP 的地方是在另個 process 中和 Nginx Event Loop 也就沒啥過係。

所以目前我也不敢保證說選那個比較好,這方面還需要更深入的探討。

那這種二模式有什麼問題呢 ?

比較大的問題是 :

它每一個 worker 同時間只能處理一個 http 請求

雖然它已經比 CGI 模式下還可以處理更多的請求,但它還是有一定的瓶頸在。

而這就是下一篇文章中要 Swoole 的原因。

參考資料

comments powered by Disqus