PHP-FPM 進程異常退出、達到 pm.max_children 限制、慢日志阻塞等導致 Nginx 返回 502 Bad Gateway。需自動化檢測並恢復。
> /var/log/php_fpm_monitor.log 2>&1
*/
$config = [
'check_url' => 'https://your-domain.com/healthz', // 健康檢查端點 (返回 200 OK)
'restart_cmd' => 'systemctl reload php-fpm', // 或 service php-fpm reload
'timeout' => 3, // HTTP 超時秒數
'max_retries' => 3, // 連續失敗幾次才重啟
'user_agent' => 'PHP-FPM-Monitor/1.0',
];
$consecutiveFailures = 0;
$stateFile = '/tmp/php_fpm_monitor_state';
// 讀取歷史失敗計數
if (file_exists($stateFile)) {
$consecutiveFailures = max(0, (int)file_get_contents($stateFile));
}
for ($i = 0; $i < $config['max_retries']; $i++) {
$ctx = stream_context_create([
'http' => [
'method' => 'HEAD',
'timeout' => $config['timeout'],
'user_agent' => $config['user_agent'],
'ignore_errors' => true, // 獲取錯誤碼不拋異常
],
'ssl' => [
'verify_peer' => false, // 自簽證書環境
'verify_peer_name' => false,
],
]);
$headers = @get_headers($config['check_url'], 1, $ctx);
if ($headers && isset($headers[0]) && stripos($headers[0], '200') !== false) {
// 成功:重置計數
$consecutiveFailures = 0;
@file_put_contents($stateFile, '0');
exit(0);
}
$consecutiveFailures++;
@file_put_contents($stateFile, (string)$consecutiveFailures);
sleep(2); // 重試間隔
}
// 達到閾值,執行重啟
if ($consecutiveFailures >= $config['max_retries']) {
error_log("[PHP-FPM Monitor] " . date('c') . " Detected 502, executing restart: {$config['restart_cmd']}");
@shell_exec($config['restart_cmd']);
// 重啟後等待並再次驗證
sleep(5);
$headers = @get_headers($config['check_url'], 1, $ctx);
if ($headers && stripos($headers[0], '200') !== false) {
error_log("[PHP-FPM Monitor] Restart successful, service recovered.");
} else {
error_log("[PHP-FPM Monitor] CRITICAL: Restart failed, manual intervention required!");
}
// 重置計數避免重複重啟
@file_put_contents($stateFile, '0');
}
| 機制 | 作用 |
|---|---|
| HEAD 請求 | 不下載 Body,極輕量,僅檢查狀態碼 |
| 連續失敗計數 | 避免網絡抖動誤觸發重啟 (文件持久化跨進程) |
| 多次重試 | 單次超時不重啟,連續 3 次失敗才動作 |
| 重啟後驗證 | 確認恢復,失敗記錄 CRITICAL 日志 |
| 忽略 SSL 驗證 | 適配自簽證測試環境,生產建議開啟驗證 |
# nginx.conf / site.conf
location = /healthz {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
或 PHP 實現 (healthz.php):
系統級替代方案:systemd Watchdog
# /etc/systemd/system/php-fpm.service.d/watchdog.conf [Service] WatchdogSec=30 Restart=on-failure RestartSec=5 # PHP-FPM 需支援 sd_notify (PHP 7.3+ 支援) # 需在 php-fpm.conf: systemd_interval = 10優點:內核級監控、無需外部腳本、精確到進程級。缺點:需 systemd 229+、PHP-FPM 編譯支援。
進階:Zabbix / Prometheus 監控告警
net.tcp.service[http,,80] + 觸發器「連續 3 次 502」→ 執行腳本重啟 + 鈉釘/郵件告警。nginx_upstream_server_status{status="down"} → 告警 → Webhook 觸發 Ansible/腳本重啟。「輕量腳本 + Crontab」適合中小項目快速落地;中大型項目建議接入系統級 Watchdog + 監控系統告警,實現自愈與可觀測並重。核心原則:**檢測業務端點而非進程存活,重啟後必須驗證恢復,避免重啟風暴**。