PHP项目健康检查与自愈

wen PHP项目 3

本文目录导读:

PHP项目健康检查与自愈

  1. 核心概念
  2. PHP项目的健康检查实现
  3. 自愈策略 (Self-Healing)
  4. 常见陷阱与最佳实践

这是一个非常关键的运维与架构设计话题,在PHP项目中,实现健康检查自愈机制,可以显著提升系统的可用性和稳定性,尤其是在生产环境或容器化部署(如Docker/K8s)中。

下面我从原理、实现层级、具体代码示例以及自愈策略四个方面为你详细拆解。


核心概念

  1. 健康检查(Health Check):定期检查系统是否能正常提供服务,通常分为两种:
    • 存活检查:进程是否还活着?比如端口是否监听,FPM是否运行。
    • 就绪检查:服务是否已经准备好接收流量?比如数据库是否连接成功,缓存是否加载完毕。
    • 业务检查:核心业务链路是否通畅?比如支付接口、队列消费速度。
  2. 自愈(Self-Healing):当健康检查发现异常时,系统自动执行恢复操作,无需人工介入。

PHP项目的健康检查实现

最简单的实现:一个独立的 health.php 文件

这是最直接的方式,由负载均衡器或K8s Probe定期请求。

<?php
// health.php - 放到项目根目录,并确保不被路由拦截
header('Content-Type: application/json');
$status = 200;
$errors = [];
// 1. 检查 PHP 本身
if (PHP_SAPI === 'cli') {
    // 如果是 CLI 模式检查,忽略
}
// 2. 检查数据库连接 (PDO)
try {
    $pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS, [
        PDO::ATTR_TIMEOUT => 3, // 3秒超时
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    ]);
    $pdo->query('SELECT 1');
} catch (\Exception $e) {
    $errors[] = 'database: ' . $e->getMessage();
}
// 3. 检查 Redis 连接
if (class_exists('\Redis')) {
    try {
        $redis = new \Redis();
        $redis->connect(REDIS_HOST, REDIS_PORT, 2.5); // 超时2.5秒
        $redis->ping();
    } catch (\Exception $e) {
        $errors[] = 'redis: ' . $e->getMessage();
    }
}
// 4. 检查磁盘空间 (防止日志写满)
$diskFree = disk_free_space('/');
if ($diskFree !== false && $diskFree < 100 * 1024 * 1024) { // 小于100MB报警
    $errors[] = 'disk: low space (' . round($diskFree / 1024 / 1024) . 'MB left)';
}
// 5. 检查队列 (如 Laravel Horizon / ThinkQueue)
// 假设你有队列心跳表或API
// $queueStatus = checkQueueWorker();
// if (!$queueStatus) $errors[] = 'queue: workers not running';
// 输出结果
if (!empty($errors)) {
    $status = 503;
    http_response_code($status);
    echo json_encode([
        'status' => 'unhealthy',
        'timestamp' => time(),
        'errors' => $errors
    ], JSON_UNESCAPED_UNICODE);
} else {
    http_response_code(200);
    echo json_encode([
        'status' => 'healthy',
        'timestamp' => time()
    ]);
}
exit(0);

更完善的方案:使用框架健康检查包

Laravel 用户强烈建议使用 spatie/laravel-healthLaravel Horizon (自带队列检查)。

composer require spatie/laravel-health
php artisan vendor:publish --provider="Spatie\Health\HealthServiceProvider" --tag="health-config"

配置后只需访问 /health 路由,即可看到美观的健康仪表盘并支持自定义检查。

容器环境下的健康检查 (Dockerfile)

如果你使用 Docker,建议直接在 Dockerfile 中集成检查命令,而不是在容器内部起一个Web Server。

FROM php:8.2-fpm
# 安装依赖...
COPY . /var/www/html
# 设置健康检查命令 (使用 CURL 或 PHP CLI)
# 注意:这里使用的是 DOCKER 的 HEALTHCHECK 指令
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost/health.php || exit 1

docker-compose.yml 中也可以配置:

services:
  php:
    image: my-php-app:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health.php"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 30s   # 给应用缓冲启动时间

机器环境(如 Supervisor):创建一个简单的 check_and_restart.sh 脚本,用 Cron 每分钟调用。

#!/bin/bash
# check_php_service.sh
URL="http://127.0.0.1:8080/health.php"
LOG="/var/log/healthcheck.log"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time 10 $URL)
if [ "$HTTP_CODE" != "200" ]; then
    echo "$(date) - Health check FAILED (code: $HTTP_CODE). Attempting restart..." >> $LOG
    # 重启 PHP-FPM
    systemctl restart php8.2-fpm
    # 或者重启 Supervisor 管理的进程
    # supervisorctl restart all
    echo "$(date) - Restart command issued." >> $LOG
else
    echo "$(date) - Health check OK." >> $LOG
fi

然后在 /etc/crontab 中添加:

*/1 * * * * root /path/to/check_php_service.sh

自愈策略 (Self-Healing)

健康检查本身只发现问题,自愈才是关键,以下策略从轻到重排列:

进程级自愈(Supervisor / Systemd)

  • 原理:PHP-FPM 或 Workerman/Swoole 进程崩溃后,进程管理器立刻拉起新进程。
  • 实施:使用 supervisord 管理你的 PHP 常驻进程(如队列消费者、Swoole Server)。
[program:php-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /path/to/artisan queue:work --sleep=3 --tries=3
autostart=true
autorestart=true          ; 核心:自动重启
user=www-data
numprocs=4                ; 多进程
redirect_stderr=true
stdout_logfile=/var/log/worker.log

容器级自愈(Docker Restart Policies & K8s)

  • Docker:设置 restart: alwaysrestart: unless-stopped
  • Kubernetes
    • livenessProbe 失败 -> K8s 杀死并重启 Pod。
    • readinessProbe 失败 -> K8s 从 Service Endpoint 中移除 Pod(不接收流量,但进程不重启)。
    • startupProbe 防止慢启动被误杀。
apiVersion: v1
kind: Pod
metadata:
  name: php-app
spec:
  containers:
  - name: php-app
    image: my-php-app:latest
    livenessProbe:
      httpGet:
        path: /health.php
        port: 8080
      initialDelaySeconds: 10
      periodSeconds: 20
      timeoutSeconds: 5
      failureThreshold: 3
    readinessProbe:
      httpGet:
        path: /health.php
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    startupProbe:
      httpGet:
        path: /health.php
        port: 8080
      initialDelaySeconds: 3
      failureThreshold: 30  # 最多等待 30s

业务逻辑自愈(代码层面)

  • 数据库重连机制
    • 连接断开后,不要直接抛异常退出,而是尝试重连 3 次,每次间隔 0.5 秒。
    • 很多 ORM(如 Eloquent/Doctrine)自带断线重连配置:'options' => [ PDO::ATTR_EMULATE_PREPARES => true, 'reconnect' => true ]
  • 队列失败重试
    • 使用 Laravel 队列的 --tries=3 参数,失败后自动重试,或进入失败表人工处理。
  • 请求限流/降级

    当健康检查发现数据库响应慢时,自动切换到缓存读取或返回友好降级页面(而非直接崩溃)。

  • 慢查询自动 Kill
    • 定期检查 SHOW FULL PROCESSLIST,找出执行时间过长的 SQL 自动 Kill。

系统级自愈(硬件/OS)

  • 磁盘空间监控:在 health.php 中检查 disk_free_space,低于阈值时自动清理历史日志文件(如 find /var/log -name "*.log" -mtime +7 -delete)。
  • 内存超限:PHP-FPM 进程内存超限时,监控脚本自动 kill -9 该进程,Supervisor 会自动重启。

常见陷阱与最佳实践

  1. 不要在生产环境暴露详细的错误信息health.php 返回的 errors 数组在外部只保留 database: connection failed,不要把密码或 IP 暴露。

  2. 健康检查路径不能有权限认证(如 Session),否则会导致检查伪失败。

  3. 健康检查本身不能依赖数据库或 Redis,如果它们挂了,你的健康检查也无法工作(自陷死局),建议只检查最基本的文件系统和进程。

    • 推荐方案:健康检查分为两个层级:
      • Liveness:只检查进程存在(如 ps aux | grep php-fpm)。
      • Readiness:检查数据库等外部依赖。
  4. 避免健康检查过重,每次检查应在 200ms 内完成,否则会影响整体响应。

  5. 自愈不能解决所有问题,如果脚本因代码死循环导致内存泄漏,重启后依然会泄漏。自愈是降级手段,最终仍需人工排查根因

  6. 日志与告警:健康检查失败时,除了自愈,一定要通过邮件、钉钉、Slack 等方式通知到人。


层级 检查方式 自愈手段
应用代码 /health.php 路由 数据库重连、队列重试、缓存降级
进程管理 Supervisor/systemd 状态 autorestart=true
容器编排 Docker HEALTHCHECK / K8s Probe 重启容器 / Pod,转移流量
基础设施 磁盘、内存、CPU 清理日志、自动扩容、自动迁移

建议落地方案

  1. 在项目中立即添加一个简单的 health.php,检查基础依赖。
  2. 如果是 Docker 环境,升级为 K8s Probe + Restart Policy。
  3. 如果是传统机器,使用 Supervisor 管理进程 + Cron 执行健康检查脚本。
  4. 加入日志监控(如 Sentry / Prometheus),实现告警链条。

这样,你的 PHP 项目就具备了一定程度的自我诊断自动恢复能力。

抱歉,评论功能暂时关闭!