PHP 的五種執行方式
網頁開發PHP 與其他程式語言一樣,都可以透過終端機輸入指令執行程式,這種方式簡單易用,但要在網頁開發中使用時,我們還需要安裝網頁伺服器。 PHP 本身並不響應實際的 HTTP 請求,那是網頁伺服器在做的事情,我們很難單純靠終端機執行 PHP 程式就完成網頁架設。 因此才需要安裝網頁伺服器 (例如:IIS、Apache、Nginx),並且進行設定,讓網頁伺服器將 HTTP 請求轉發給 PHP 處理,最後接收處理結果回傳給使用者。
您可以簡單理解成 PHP 只是用來產生 HTML 給網頁伺服器回傳給使用者,而在網頁伺服器與 PHP 之間進行溝通的東西,稱為伺服器應用程式接口 (Server Application Programming Interface, SAPI),您可能會在其他地方看到 ISAPI 或 NSAPI,其實都是差不多的東西。 PHP 常見的 SAPI 有 CLI、CGI、mod_php、FastCGI 及 PHP-FPM。
認識行程 (Process) 與執行緒 (Thread)
在了解各項 PHP SAPI 之前,需要先了解行程及執行緒兩個概念。
- 行程 (Process)
- 指電腦中已經在執行並且載入到記憶體中的程式,隨時都能被 CPU 執行,在工作管理員 (Windows) 或活動監控器 (macOS) 或 jobs (Linux) 可以看到電腦正在執行的所有行程。 每個行程在建立時,系統會分配一塊獨立的記憶體空間給行程使用,因此不同行程之間要共享資料較為困難。
- 執行緒 (Thread)
- 一個行程是由一個或多個執行緒組成,也就是說行程是執行緒的容器,執行緒是程式實際執行時的執行單位。在多執行緒中,每個執行緒隸屬同一個行程,也共用同樣的記憶體空間,所以多個執行緒之間要共享資料較為容易,可以想像為程式中的全域變數。當多個執行緒同時操作相同的資料時,必需確保執行緒安全,使程式功能正確完成。
在這裡不用詳細了解行程與執行緒的細節,只要知道兩個重點:
- 一個行程是由一個或多個執行緒組成。
- 在多執行緒運行時,需確保執行緒安全。
執行緒安全
那什麼是執行緒安全 (Thread Safe) 呢?執行緒安全是指某個函數同時被多個執行緒呼叫時,能夠正確地處理公用變數資料,使程式功能夠正確完成。來看看一段有問題的 Java 程式碼:
public class Main {
private static int count;
public static void increase() {
count++;
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
increase();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
increase();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(count);
}
}
以上程式碼,當兩個執行緒都執行完成後,理論上應該輸出 200000 才對,但實際狀況卻是每次執行答案都不一樣,這就是沒有執行緒安全造成的問題。 解決方式也很簡單,只要當 increase() 函數被呼叫時鎖定不讓其它執行緒呼叫即可,這就是執行緒安全的作法,但此方式會減慢執行速度,因此也不能將所有函數都加上鎖定,應謹慎評估使用。
執行方式 1:命令列介面 (CLI)
命令列介面(Command-Line Interface, CLI)是在圖形化介面之前最廣泛的使用者介面,絕大部分的程式語言都支援 CLI 介面執行程式,當然 PHP 也不例外。不過 CLI 在網頁開發用處不大,您很難單純依靠 CLI 指令完成網頁架設。
使用方式
直接開啟命令提示字元 (CMD) 輸入 php 加檔名即可。
php index.php
執行方式 2:通用閘道器介面 (CGI)
通用閘道器介面 (Common Gateway Interface, CGI) 是為網路服務提供生成動態內容的程式,CGI 在執行時,每處理一個 HTTP 請求都會產生一個行程 (Process) 來生成內容 (通常為 HTML),當 HTTP 請求處理完成後,行程便會關閉,有點類似執行了一次 CLI 指令。 該模式簡單容易實現,缺點就是執行速度慢,每次執行都需要開啟關閉一次行程。
使用方式
CGI 模式需要搭配網頁伺服器才能使用,以下使用 Apache 作為範例,打開 httpd.conf 設定檔,根據以下內容進行修改,修改完成後重新啟動服務。
- Apache 安裝路徑:C:/Web/Apache24
- PHP 安裝路徑:C:/Web/php
- 網頁放置路徑:C:/Web/html
ServerRoot "C:/Web/Apache24"
DocumentRoot "C:/Web/html/"
<Directory "C:/Web/html/">
Options None
AllowOverride None
Require all granted
</Directory>
<Directory "C:/Web/php/">
Options None
AllowOverride None
Require all granted
</Directory>
<IfModule mime_module>
AddType application/x-httpd-php .php
</IfModule>
<IfModule alias_module>
ScriptAlias /cgi-bin/ "C:/Web/php/"
</IfModule>
Action application/x-httpd-php "/cgi-bin/php-cgi.exe"
執行方式 3:mod_php
把 PHP 整合至 Apache 中,變成 Apache 的一個模組,沒有使用額外的主行程 (Process) 來處理,通通由 Apache 主行程處理。 在處理大流量時,Apache 主行程會生成多個子行程同步處理,以提升處理速度,通常在 Apache 執行時,就已經生成一定數量的子行程,等待 HTTP 請求,避免像 CGI 模式需要頻繁的開關行程。生成子行程的方式還區分 prefork、worker、event 及 winnt 等不同模式,這些模式稱為多行程模式 (Multi-Processing Module, MPM)。
- prefork 模式
- 多個子行程,每個子行程只有單一執行緒,同一時間只能處理一個 HTTP 請求,是一個成熟穩定的模式,也是唯一沒有執行緒安全問題的 MPM 模式,但此模式不擅長處理大流量。
- worker 模式
- 多個子行程,每個子行程都是多執行緒,在大流量的狀況下,會比 prefork 模式擁有更多可用的執行緒,效能表現更加優秀。 該模式在使用 keep-alive 長連線時,會一直佔用某個執行緒,即使沒有產生任何的請求,也需要等到超時才會釋放執行緒,該問題在 prefork 模式也存在。
- event 模式
- 多個子行程,每個子行程都是多執行緒,是 Apache 最新的 MPM 模式,與 worker 模式非常像,但解決了 keep-alive 長連線造成的問題。 該模式需要運行在較新的 Linux 平台,在缺乏良好執行緒設計的舊平台表現不太理想。
- winnt 模式
- 一個子行程,多執行緒,是專門針對 Windows 設計的 MPM 模式。
這種方式的優點是執行速度比 CGI 快很多,但安全性較低,且 PHP 與 Apache 耦合度較高,無法將 PHP 處理程式與網頁伺服器分別架設在不同主機,早期 PHP 大多都使用這種模式。
確認 Apache MPM 模式
要查看目前 Apache 是使用哪一種 MPM 模式,可以執行以下指令,並找到 Server MPM。
// Windows 使用指令
httpd.exe -V
... (省略) ...
Server MPM: WinNT
threaded: yes (fixed thread count)
// Linux / macOS 使用指令
httpd -V
... (省略) ...
Server MPM: prefork
threaded: no
使用方式
mod_php 模式設定非常簡單,以下使用 Apache 作為範例,打開 httpd.conf 設定檔,根據以下內容進行修改,修改完成後重新啟動服務。
- Apache 安裝路徑:C:/Web/Apache24
- PHP 安裝路徑:C:/Web/php (需為執行緒安全版本)
- 網頁放置路徑:C:/Web/html
ServerRoot "C:/Web/Apache24"
DocumentRoot "C:/Web/html/"
<Directory "C:/Web/html/">
Options None
AllowOverride None
Require all granted
</Directory>
LoadModule php_module C:/Web/php/php8apache2_4.dll
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
# PHP 設定檔所在目錄
PHPIniDir "C:\Web\php"
執行方式 4:FastCGI
快速通用閘道器介面 (Fast Common Gateway Interface, FastCGI) 是 CGI 的增強版,改善了 CGI 在執行時,行程一開一關的問題,也改善了 mod_php 與網頁伺服器耦合度較高的問題。 FastCGI 執行速度不亞於 mod_php,同樣採用多行程常駐開啟方式,且每個行程使用單一執行緒,等待 HTTP 網址請求,更重要的是 FastCGI 與網頁伺服器可以分別架設在不同主機上,兩者再透過 TCP 進行溝通,這對大流量網站非常重要。 FastCGI 網頁伺服器耦合度較低,是目前在 Windows 執行 PHP 最快的模式,缺點則是設定較為複雜。
使用方式
在 Windows 使用 Apache 執行 FastCGI 模式有很多種方式,其中一種方式是使用 fcgid_module 模組,該模組在 Apache 2.4 需額外下載才能使用,且不支援 TCP 模式 (要支援 TCP 需改用 mod_proxy_fcgi 模組)。
以下是一個相當簡單的範例,切勿在實際環境使用。打開 httpd.conf 設定檔,根據以下內容進行修改,修改完成後重新啟動服務。
- Apache 安裝路徑:C:/Web/Apache24
- PHP 安裝路徑:C:/Web/php
- 網頁放置路徑:C:/Web/html
ServerRoot "C:/Web/Apache24"
DocumentRoot "C:/Web/html/"
<Directory "C:/Web/html/">
Options None
AllowOverride None
Require all granted
</Directory>
LoadModule fcgid_module modules/mod_fcgid.so
<IfModule fcgid_module>
FcgidInitialEnv PHPRC "C:/Web/php"
<Files ~ "\.php$">
Options Indexes FollowSymLinks ExecCGI
AddHandler fcgid-script .php
FcgidWrapper "C:/Web/php/php-cgi.exe" .php
</Files>
</IfModule>
執行方式 5:PHP-FPM
PHP-FPM 是 FastCGI 的行程管理器,可以自行管理行程的數量,速度更快也更穩定,所以嚴格來說 PHP-FPM 並不等於 FastCGI,這兩者很常混為一談。此外,這兩個執行方式都是多行程 (Process),每個行程單一執行緒 (Thread)。
PHP-FPM 是現在 PHP 最常使用的執行模式,效能提升非常顯著,在 Linux 及 macOS 作業系統是首選,但卻不支援 Windows 作業系統,這似乎牽涉到 PHP-FPM 核心設計問題,未來也不一定會支援。
使用方式
由於 Windows 不支援 PHP-FPM,以下是在 macOS 使用 Nginx 網頁伺服器的設定。 必須先啟動 PHP-FPM 服務,接下來打開 nginx.conf 設定檔,根據以下內容進行修改,修改完成後重新啟動服務。
- PHP-FPM 位址:127.0.0.1:9000
- 網頁放置路徑:/var/www/html
upstream php_fpm {
server 127.0.0.1:9000;
}
server {
listen 80;
server_name localhost;
index index.php index.htm index.html;
root /var/www/html;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SERVER_SOFTWARE nginx;
fastcgi_index index.php;
fastcgi_pass php_fpm;
fastcgi_connect_timeout 600;
fastcgi_read_timeout 600;
fastcgi_send_timeout 600;
}
}
Non Thread Safe 與 Thread Safe
在 Windows 架設 PHP 執行環境時,細心的朋友,應該會發現在下載 PHP 的時候,有分執行緒安全 (Thread Safe) 與非執行緒安全 (Non Thread Safe) 兩個版本。根據上述的說明,我們知道當 PHP 運行在多執行緒環境時,才需要使用執行緒安全 (Thread Safe) 版本。
下表為 PHP SAPI 的總整理。
SAPI | 行程 | 每個行程執行緒數量 | 獨立主機架設 | 建議 | |
---|---|---|---|---|---|
CLI | 單行程 | 單執行緒 | 不適用 | ||
CGI | 多行程 | 單執行緒 | 否 | ||
mod_php | |||||
prefork | 單主行程 多子行程 |
單執行緒 | 否 | ||
worker | 單主行程 多子行程 |
多執行緒 | 否 | ||
event | 單主行程 多子行程 |
多執行緒 | 否 | ||
winnt | 單主行程 單子行程 |
多執行緒 | 否 | ||
FastCGI | 多行程 | 單執行緒 | 可 | Windows 首選 | |
PHP-FPM | 多行程 | 單執行緒 | 可 | Linux 首選 macOS 首選 |
由上表大概就能看出,在大部分情況下,PHP 都是單一執行緒的,只有使用 mod_php 模式時,才需要下載執行緒安全 (Thread Safe) 版本,其他執行模式通通都是單一執行緒,使用非執行緒安全 (Non Thread Safe) 版本即可。
1 則留言