在分散式架構下,單一慢查詢、外部 API 無回應、大檔上傳等場景,若缺乏超時機制,會導致 PHP-FPM 進程堵塞、連接池耗盡,引發「雪崩效應」拖垮整個服務。本文系統梳理 PHP 生態各層級的超時配置與最佳實踐。
核心指令 FastCgiExternalServer 參數 -idle-timeout(單位:秒):
<IfModule mod_fastcgi.c>
FastCgiExternalServer /path/to/php-cgi -socket /path/to/php-fpm.sock -idle-timeout 100
ScriptAlias /fcgi-bin/ "/path/to/cgi-bin/"
AddHandler php-fastcgi .php
Action php-fastcgi /fcgi-bin/php-cgi
</IfModule>
FastCGI: comm with server aborted: idle timeoutIPCCommTimeout(通訊最長時間)、ProcessLifeTime(進程最大壽命)、MaxRequestsPerProcess(進程處理請求上限)http {
# FastCGI 專用(php-fpm)
fastcgi_connect_timeout 5s; # 連線階段
fastcgi_send_timeout 10s; # 傳送請求體
fastcgi_read_timeout 300s; # 讀取回應(核心:PHP 執行時間)
# Proxy/Upstream 專用(反向代理至 Java/Go/Node 等)
proxy_connect_timeout 15s;
proxy_read_timeout 60s;
proxy_send_timeout 10s;
}
重點:fastcgi_read_timeout決定 PHP 腳本最大執行時間,必須大於php-fpm.conf的request_terminate_timeout及php.ini的max_execution_time,否則 Nginx 會先切斷連線導致 504 Gateway Timeout。
核心參數(單位:秒):
server.max-read-idle:讀請求閒置上限(預設 60)server.max-write-idle:寫回應閒置上限(預設 360)server.read-timeout / server.write-timeout:整體讀寫逾時(0 表不限制)server.max-connection-idle:Keep-Alive 連線最大閒置配置檔 php-fpm.conf(或 pool.d/www.conf):
; 請求硬性終止時間(0 表關閉,單位:秒/分/時/天)
request_terminate_timeout = 300s
; 慢日誌觸發閾值(建議開啟,排查效能瓶頸)
request_slowlog_timeout = 10s
slowlog = /var/log/php-fpm/www-slow.log
關鍵區別:request_terminate_timeout是 FPM 進程層面的硬殺掉,max_execution_time(php.ini)僅在mod_php模式生效,php-fpm 模式下完全被忽略。
// php.ini 全域設定(僅 mod_php 生效)
max_execution_time = 30
// 運行時動態調整(cli 模式預設 0 不限制)
ini_set('max_execution_time', 300);
set_time_limit(300); // 等效,重置計時器
// 註意:safe_mode=On 時以上設定失效
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10, // 總超時(秒)
CURLOPT_TIMEOUT_MS => 5000, // 總超時(毫秒,需 CURLOPT_NOSIGNAL=1)
CURLOPT_CONNECTTIMEOUT => 3, // 連線階段超時(秒)
CURLOPT_CONNECTTIMEOUT_MS => 2000, // 連線階段超時(毫秒)
CURLOPT_DNS_CACHE_TIMEOUT => 120, // DNS 快取秒數
CURLOPT_NOSIGNAL => 1, // 多執行緒環境下啟用毫秒級超時必備
]);
避坑指南:libcurl >= 7.21.0才可靠支援毫秒級超時;curl_multi並行時毫秒超時不準,建議僅用於單請求。
$ctx = stream_context_create([
'http' => [
'timeout' => 5.0, // 總超時(秒,支援浮點數)
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
]);
$data = file_get_contents('https://api.example.com', false, $ctx);
$fp = fsockopen('example.com', 80, $errno, $errstr, 3.0); // 連線超時 3 秒
stream_set_blocking($fp, true); // 同步阻塞模式
stream_set_timeout($fp, 5, 0); // 讀寫超時 5 秒
$meta = stream_get_meta_data($fp);
while (!feof($fp) && !$meta['timed_out']) {
$data .= fgets($fp, 4096);
$meta = stream_get_meta_data($fp); // 每次迴圈檢查 timed_out
}
if ($meta['timed_out']) throw new RuntimeException('Socket read timeout');
| 客戶端 | 連線超時 | 讀/寫超時 | 備註 |
|---|---|---|---|
| PDO MySQL | PDO::ATTR_TIMEOUT => 5 | 無原生支援,需自行實作 | 僅連線階段 |
| mysqli | mysqli_init() + options(MYSQLI_OPT_CONNECT_TIMEOUT, 5) | mysqli_options($link, MYSQLI_OPT_READ_TIMEOUT, 10) | 需 mysqlnd 驅動 |
| Redis (phpredis) | connect('host', 6379, 2.5) | setOption(Redis::OPT_READ_TIMEOUT, 1.5) | 支援浮點秒 |
| Memcached | setOption(Memcached::OPT_CONNECT_TIMEOUT, 2000) | setOption(Memcached::OPT_POLL_TIMEOUT, 1000) | 單位:毫秒 |
| 層級 | 關鍵配置 | 建議值 | 說明 |
|---|---|---|---|
| Nginx | fastcgi_read_timeout | 300s | 大於 PHP-FPM 設定 |
| PHP-FPM | request_terminate_timeout | 240s | 硬性殺掉失控進程 |
| PHP-FPM | request_slowlog_timeout | 10s | 記錄慢請求回溯堆疊 |
| cURL | CURLOPT_TIMEOUT / _MS | 5-10s / 3000-5000ms | 依外部 API SLA 設定 |
| DB/Cache | 連線/讀寫超時 | 1-3s / 3-5s | 嚴格控制,防止單點拖垮 |
CURLOPT_CONNECTTIMEOUT 不包含 DNS,需額外設定 CURLOPT_DNS_CACHE_TIMEOUT 或使用 IP 直連。client_max_body_size(Nginx)、post_max_size/upload_max_filesize(PHP)、fastcgi_request_buffering off(避免緩衝到磁碟)。超時控制是系統穩定性的基石。建立「分層設定、層層遞減、全鏈路可觀測」的超時體系,配合慢日誌分析、鏈路追蹤,才能在高併發、微服務架構下守住可用性底線。