PHP-FPM 進程佔用 CPU 100% 導致 502 Bad Gateway、響應極慢,是高併發 PHP 應用的典型故障。本文從代碼層、配置層、架構層三個維度,系統性剖析根因並給出生產級解決方案。

| 類別 | 典型特徵 | 根因 | 排查入口 |
|---|---|---|---|
| 代碼死循環/長耗時 | 特定 URL 持續高 CPU | 死循環、遞歸無出口、大數據未分頁處理 | slowlog、xhprof、strace |
| 阻塞式網絡調用 | 所有進程高 CPU、網絡等待 | file_get_contents/curl 無超時、外部 API 響應慢 | slowlog、netstat、tcpdump |
| 正則回溯災難 | 特定輸入觸發 CPU 飆升 | 惡意正則 + 惡意輸入(ReDoS) | 代碼審計、正則測試 |
| 無限掛載/包含 | 啟動後漸漸升高 | include 循環、自動加載器缺陷 | strace -p PID |
| 惡意代碼/挖礦 | 無業務流量也高 CPU | Webshell、挖礦腳本、反彈 Shell | 掃描 webshell、進程樹分析 |
// ❌ 危險:無超時,卡死進程
$content = file_get_contents('https://api.example.com/data');
// ✅ 安全:流上下文設置超時
$ctx = stream_context_create([
'http' => ['timeout' => 3] // 3 秒超時
]);
$content = file_get_contents('https://api.example.com/data', 0, $ctx);
// ✅ 更強大:Guzzle/cURL 異步+超時+重試
$client = new GuzzleHttp\Client(['timeout' => 3.0]);
$response = $client->get('https://api.example.com/data');
// 限制回溯次數 (PCRE 8.32+)
ini_set('pcre.backtrack_limit', 100000);
ini_set('pcre.recursion_limit', 100000);
// 避免嵌套量詞:(a+)+、(.*)* 等
// 使用具體字符類代替 .*
; php-fpm.conf / www.conf
request_slowlog_timeout = 5s # 超過 5 秒記錄慢請求
slowlog = /usr/local/php/var/log/slow.log
# 分析慢日誌
grep -A 20 "script_filename" /usr/local/php/var/log/slow.log
; php-fpm.conf
; 默認 0s 表示關閉,必須設置!
request_terminate_timeout = 30s
; 進程管理模式優化
pm = dynamic
pm.max_children = 100
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30
pm.max_requests = 500 # 防止內存洩漏,定期回收進程
原理: request_terminate_timeout 是 FPM 進程管理器強制 kill 超時進程的保護網,防止單個請求佔用進程導致進程池耗盡。但這只能治標,長請求仍會消耗 CPU。
; php.ini
disable_functions = exec,system,shell_exec,passthru,popen,proc_open,pcntl_exec,eval,assert
| 問題類型 | 架構解決方案 | 技術棧示例 |
|---|---|---|
| 外部 API 調用慢 | 異步隊列 + 消費者模式 | Laravel Queue + Redis/RabbitMQ, Swoole 協程 |
| 大數據計算/報表 | 離線預計算 + 緩存 | Crontab + Redis/Memcached, ClickHouse |
| 圖片/視頻處理 | 對象存儲觸發異步處理 | OSS/S3 + 函數計算/消息隊列 |
| 高併發寫入 | 寫入分離 + 批量提交 | Kafka + 批量消費入庫 |
# 1. 找出高 CPU 進程
top -H -p $(pgrep -d',' php-fpm)
# 2. 追蹤進程系統調用
strace -p PID -c
strace -p PID -f -o trace.log
# 3. 查看 PHP 執行堆棧
gdb -p PID -ex "bt" -batch
# 4. 檢查打開文件
ls -l /proc/PID/fd/
# 5. 網絡連接狀態
netstat -anp | grep PID
PHP-CGI CPU 100% 的鐵律:**「無超時不調用外部資源,無限制不寫正則,無異步不做耗時任務」**。優先序:**代碼層修復(加超時、限回溯) → 配置層防護(request_terminate_timeout、max_requests) → 架構層重構(異步隊列、預計算)**。建立 slowlog 常態化分析機制,將故障發現從「用戶投訴」前移到「監控預警」。