本文目录导读:

我来提供一个完整的 PHP 健康检查失败告警方案,包括监控脚本、告警配置和通知机制。
健康检查脚本方案
基础健康检查脚本
<?php
/**
* PHP健康检查脚本
* 支持命令行和Web访问两种方式
*/
class HealthChecker {
private $config;
public function __construct() {
$this->config = [
'php_version_min' => '7.4',
'memory_limit_min' => '128M',
'disk_space_min' => 1024, // MB
'cache_backend' => 'redis',
'services' => [
'redis' => ['host' => '127.0.0.1', 'port' => 6379],
'mysql' => ['host' => '127.0.0.1', 'port' => 3306],
'rabbitmq' => ['host' => '127.0.0.1', 'port' => 5672],
],
'webhook_url' => getenv('ALERT_WEBHOOK_URL') ?: '',
'notification_services' => [
'slack' => getenv('SLACK_WEBHOOK_URL') ?: '',
'dingtalk' => getenv('DINGTALK_WEBHOOK_URL') ?: '',
'email' => getenv('ADMIN_EMAIL') ?: '',
]
];
}
public function check() {
$results = [];
$healthy = true;
// 基础环境检查
$results['php_version'] = $this->checkPhpVersion();
$results['php_extensions'] = $this->checkPhpExtensions();
$results['memory'] = $this->checkMemory();
$results['disk'] = $this->checkDiskSpace();
$results['permissions'] = $this->checkFilePermissions();
// 服务检查
$results['services'] = $this->checkServices();
// 应用检查
$results['application'] = $this->checkApplication();
// 汇总结果
foreach ($results as $category => $checks) {
foreach ($checks as $check => $result) {
if (!$result['healthy']) {
$healthy = false;
$this->handleFailure($check, $result);
}
}
}
return ['healthy' => $healthy, 'checks' => $results];
}
private function checkPhpVersion() {
$current = PHP_VERSION;
return [
'healthy' => version_compare($current, $this->config['php_version_min'], '>='),
'current' => $current,
'expected' => $this->config['php_version_min']
];
}
private function checkPhpExtensions() {
$required = ['pdo', 'redis', 'json', 'mbstring', 'curl'];
$result = ['healthy' => true, 'missing' => []];
foreach ($required as $ext) {
if (!extension_loaded($ext)) {
$result['healthy'] = false;
$result['missing'][] = $ext;
}
}
return $result;
}
private function checkMemory() {
$limit = ini_get('memory_limit');
return [
'healthy' => $this->parseMemoryLimit($limit) >= $this->config['memory_limit_min'],
'current' => $limit,
'expected' => $this->config['memory_limit_min']
];
}
private function checkDiskSpace() {
$free = disk_free_space("/");
$freeMB = $free != false ? round($free / 1024 / 1024, 2) : 0;
return [
'healthy' => $freeMB >= $this->config['disk_space_min'],
'free_space_mb' => $freeMB,
'expected' => $this->config['disk_space_min']
];
}
private function checkFilePermissions() {
$dirs = ['/var/log', '/tmp', '/var/www/html/storage'];
$result = ['healthy' => true, 'issues' => []];
foreach ($dirs as $dir) {
if (file_exists($dir) && !is_writable($dir)) {
$result['healthy'] = false;
$result['issues'][] = $dir . ' is not writable';
}
}
return $result;
}
private function checkServices() {
$result = ['healthy' => true, 'services' => []];
foreach ($this->config['services'] as $name => $config) {
$connect = @fsockopen($config['host'], $config['port'], $errno, $errstr, 3);
$healthy = $connect !== false;
if ($connect) {
fclose($connect);
}
$result['services'][$name] = [
'healthy' => $healthy,
'error' => $healthy ? null : $errstr
];
if (!$healthy) {
$result['healthy'] = false;
}
}
return $result;
}
private function checkApplication() {
// 应用特定检查,可扩展
$result = ['healthy' => true, 'checks' => []];
// 检查缓存
$result['checks']['cache'] = $this->checkCache();
// 检查队列
$result['checks']['queue'] = $this->checkQueue();
// 检查数据库连接
$result['checks']['database'] = $this->checkDatabase();
return $result;
}
private function parseMemoryLimit($limit) {
$unit = strtolower(substr($limit, -1));
$value = (int)$limit;
switch ($unit) {
case 'g': return $value * 1024;
case 'm': return $value;
case 'k': return $value / 1024;
default: return $value;
}
}
private function handleFailure($check, $result) {
$message = $this->formatAlertMessage($check, $result);
$this->sendAlert($message);
$this->logFailure($message);
}
private function sendAlert($message) {
if ($this->config['notification_services']['slack']) {
$this->sendSlackAlert($message);
}
if ($this->config['notification_services']['dingtalk']) {
$this->sendDingTalkAlert($message);
}
if ($this->config['notification_services']['email']) {
$this->sendEmailAlert($message);
}
}
private function sendSlackAlert($message) {
$data = json_encode([
'text' => ":warning: *PHP Health Check Alert*\n$message"
]);
$this->postToWebhook($this->config['notification_services']['slack'], $data);
}
private function sendDingTalkAlert($message) {
$data = json_encode([
'msgtype' => 'markdown',
'markdown' => [
'title' => 'PHP健康检查告警',
'text' => "### PHP健康检查告警\n\n$message"
]
]);
$this->postToWebhook($this->config['notification_services']['dingtalk'], $data);
}
private function sendEmailAlert($message) {
$subject = '[告警] PHP健康检查失败';
mail($this->config['notification_services']['email'], $subject, $message);
}
private function postToWebhook($url, $data) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_exec($ch);
curl_close($ch);
}
private function formatAlertMessage($check, $result) {
$message = "检查项: $check\n";
$message .= "结果: " . ($result['healthy'] ? '正常' : '异常') . "\n";
if (isset($result['current'])) {
$message .= "当前值: {$result['current']}\n";
}
if (isset($result['expected'])) {
$message .= "期望值: {$result['expected']}\n";
}
if (isset($result['error'])) {
$message .= "错误信息: {$result['error']}\n";
}
return $message;
}
private function logFailure($message) {
$logFile = '/var/log/php-healthcheck.log';
$timestamp = date('Y-m-d H:i:s');
file_put_contents($logFile, "[$timestamp] $message\n", FILE_APPEND);
}
}
// 执行检查
$checker = new HealthChecker();
$result = $checker->check();
// CLI模式下输出结果
if (PHP_SAPI === 'cli') {
echo json_encode($result, JSON_PRETTY_PRINT) . "\n";
exit($result['healthy'] ? 0 : 1);
}
// Web访问模式
header('Content-Type: application/json');
http_response_code($result['healthy'] ? 200 : 503);
echo json_encode($result, JSON_PRETTY_PRINT);
Shell 监控脚本
#!/bin/bash
# healthcheck_wrapper.sh - 包装脚本用于定期监控
#!/bin/bash
# healthcheck_wrapper.sh - 包装脚本用于定期监控
# 配置
PHP_BIN="/usr/bin/php"
SCRIPT_PATH="/var/www/html/healthcheck.php"
LOG_FILE="/var/log/php-healthcheck.log"
ALERT_LOG="/var/log/php-healthcheck-alerts.log"
WEBHOOK_URL_ENV="ALERT_WEBHOOK_URL"
MAX_RETRIES=3
RETRY_INTERVAL=60 # 秒
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
echo -e "$1"
}
# 执行健康检查
run_healthcheck() {
local output
local exit_code
output=$($PHP_BIN "$SCRIPT_PATH" 2>&1)
exit_code=$?
if [ $exit_code -ne 0 ]; then
log_message "${RED}健康检查失败${NC}"
log_message "$output"
return 1
else
log_message "${GREEN}健康检查通过${NC}"
echo "$output" | jq . >> "$LOG_FILE" 2>/dev/null || true
return 0
fi
}
# 重试逻辑
retry_check() {
local attempts=0
local max_retries=$MAX_RETRIES
while [ $attempts -lt $max_retries ]; do
if run_healthcheck; then
return 0
fi
attempts=$((attempts + 1))
log_message "重试 $attempts/$max_retries (等待${RETRY_INTERVAL}秒)"
sleep $RETRY_INTERVAL
done
return 1
}
# 发送告警
send_alert() {
local alert_message="PHP健康检查连续${MAX_RETRIES}次失败"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# 记录告警
echo "[$timestamp] $alert_message" >> "$ALERT_LOG"
# 发送到webhook
if [ ! -z "$WEBHOOK_URL_ENV" ]; then
curl -X POST -H 'Content-Type: application/json' \
-d "{\"text\": \"🚨 $alert_message\n时间: $timestamp\"}" \
"$WEBHOOK_URL_ENV"
fi
# 发送邮件(如果配置了mail命令)
if command -v mail &> /dev/null; then
echo "$alert_message" | mail -s "PHP健康检查告警" "$ADMIN_EMAIL" 2>/dev/null
fi
}
# 主函数
main() {
log_message "开始执行健康检查..."
if retry_check; then
log_message "健康检查最终结果: 正常"
return 0
else
log_message "健康检查最终结果: 失败"
send_alert
return 1
fi
}
# 运行
main
exit $?
定时任务配置
# crontab 配置示例(每5分钟执行一次) */5 * * * * /usr/bin/php /var/www/html/healthcheck.php --quiet > /dev/null 2>&1 # 或使用Shell脚本(每5分钟检查,失败则立即告警) */5 * * * * /usr/local/bin/healthcheck_wrapper.sh >> /var/log/php-healthcheck-cron.log 2>&1 # 更复杂的监控(每5分钟常规检查,每小时详细检查) */5 * * * * /usr/local/bin/healthcheck_wrapper.sh --quick 0 * * * * /usr/local/bin/healthcheck_wrapper.sh --full # 启动时检查 @reboot /usr/local/bin/healthcheck_wrapper.sh
监控平台集成
Prometheus 导出器
<?php
// prometheus_exporter.php - Prometheus metrics 导出器
class PrometheusExporter {
private $healthChecker;
public function __construct() {
$this->healthChecker = new HealthChecker();
}
public function export() {
$result = $this->healthChecker->check();
$metrics = [];
// 基础指标
$metrics[] = '# HELP php_health_check_total PHP Health Check Status';
$metrics[] = '# TYPE php_health_check_total gauge';
$metrics[] = "php_health_check_total " . ($result['healthy'] ? 1 : 0);
// 服务状态
foreach ($result['checks']['services']['services'] as $name => $status) {
$metrics[] = "# HELP php_service_{$name} Status of {$name} service";
$metrics[] = "# TYPE php_service_{$name} gauge";
$metrics[] = "php_service_{$name} " . ($status['healthy'] ? 1 : 0);
}
// 资源使用
$metrics[] = '# HELP php_memory_usage Current memory usage in MB';
$metrics[] = '# TYPE php_memory_usage gauge';
$metrics[] = "php_memory_usage " . memory_get_usage(true) / 1024 / 1024;
return implode("\n", $metrics);
}
}
header('Content-Type: text/plain; version=0.0.4');
$exporter = new PrometheusExporter();
echo $exporter->export();
Grafana 告警规则
# grafana-alerts.yml
apiVersion: 1
groups:
- name: PHP Health Alerts
rules:
- alert: PHPHealthCheckFailed
expr: php_health_check_total == 0
for: 5m
labels:
severity: critical
annotations:
summary: "PHP Health Check Failed"
description: "PHP application health check has been failing for 5 minutes"
- alert: PHPHighMemoryUsage
expr: php_memory_usage_percent > 85
for: 10m
labels:
severity: warning
annotations:
summary: "High PHP Memory Usage"
description: "PHP memory usage is above 85%"
- alert: PHPRedisDown
expr: php_service_redis == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Redis Service Down"
description: "Redis service is not responding"
告警通知配置
Slack 告警配置
// slack-webhook-config.json
{
"channel": "#php-alerts",
"username": "HealthBot",
"icon_emoji": ":ghost:",
"text": "PHP Monitoring System",
"attachments": [
{
"color": "danger",
"title": "PHP Health Check Failed",
"fields": [
{
"title": "Environment",
"value": "production",
"short": true
},
{
"title": "Timestamp",
"value": "2024-01-01 12:00:00",
"short": true
}
],
"actions": [
{
"type": "button",
"text": "View Dashboard",
"url": "http://monitoring.example.com"
}
]
}
]
}
钉钉/企业微信告警配置
{
"msgtype": "markdown",
"markdown": {: "PHP监控告警",
"text": "## ⚠️ PHP健康检查告警\n\n**检查项:** PHP Version\n**状态:** ❌ 异常\n**详情:** 版本过低\n\n[查看监控面板](http://monitoring.example.com)"
},
"at": {
"isAtAll": false,
"atMobiles": ["13800138000"]
}
}
最佳实践建议
- 分级告警:区分紧急(服务宕机)和警告(资源使用率高)
- 告警聚合:避免重复告警,设置聚合窗口
- 自动恢复:实现简单的自动重启脚本
- 监控可视化:使用 Grafana 展示趋势
- 告警分流:根据服务重要性分配不同通知渠道
- 留档记录:保存历史告警记录用于分析
测试命令
# 手动测试健康检查 curl http://localhost/healthcheck.php # 测试告警 curl -X POST http://localhost/healthcheck.php --test-alert # 查看日志 tail -f /var/log/php-healthcheck.log # 强制触发告警(模拟故障) PHP_ALERT_TEST=true php healthcheck.php
这个方案提供了完整的监控告警体系,可以根据实际需要调整和扩展,记得根据你的部署环境调整路径、端口和通知渠道配置。