本文目录导读:

- 健康检查的核心层级
- 基础方案:简单脚本(适用于小型项目)
- 进阶方案:结构化健康检查(推荐)
- 线上生产环境的注意事项
- 进阶场景:深度业务检查(Deep Health)
- 监控集成建议
- 完整代码示例(原生PHP,无框架)
- 总结建议
在 PHP 项目中实施健康检查,关键在于分层设计和按需求关注点来构建,下面我从基础到进阶,结合实战代码,为你梳理一套完整的健康检查方案。
健康检查的核心层级
健康检查分为三个层次,你可以根据项目规模选择:
| 层级 | 目的 | 触发频率 | |
|---|---|---|---|
| Liveness | 进程是否存活、PHP-FPM是否响应 | 确认服务没死 | 高频(每10秒) |
| Readiness | 数据库连接、Redis、外部API连通性 | 确认服务能否处理请求 | 中频(每30秒) |
| Deep Health | 核心业务逻辑(如订单写入测试)、磁盘IO、队列堆积情况 | 确认“业务”是否健康 | 低频(每分钟) |
核心原则:必须使用
HTTP接口返回JSON,并配合正确的HTTP状态码(200/503)。
基础方案:简单脚本(适用于小型项目)
这是最快速的实现方式,直接创建一个路由或脚本文件。
创建检查文件 health.php(放置在Web根目录):
<?php
// 简单的健康检查:只检查PHP环境和应用是否能引导起来
require_once __DIR__ . '/vendor/autoload.php'; // 引入启动文件(如果有)
$health = [
'status' => 'ok',
'time' => date('c'),
'checks' => []
];
// 检查1:PHP版本
$health['checks']['php_version'] = phpversion();
// 检查2:类是否加载成功(如果框架核心类加载不了,这里会直接500错误)
if (!class_exists('YourFramework\Application')) {
http_response_code(503);
$health['status'] = 'error';
echo json_encode($health);
exit;
}
if ($health['status'] === 'ok') {
http_response_code(200);
} else {
http_response_code(503);
}
header('Content-Type: application/json');
echo json_encode($health);
缺点:只能检测应用能否启动,无法检测依赖服务(DB/Redis)是否可用。
进阶方案:结构化健康检查(推荐)
此方案关注 Readiness 和 Deep Health,适合中大型项目。
步骤 1:定义检查器接口
<?php
// app/Contracts/HealthCheckInterface.php
interface HealthCheckInterface
{
/**
* 执行健康检查
* @return array ['status' => 'ok'|'error', 'details' => '附加信息(如延迟ms)']
*/
public function check(): array;
}
步骤 2:实现具体检查类
示例A:数据库连接检查(MySQL / PostgreSQL)
<?php
// app/HealthChecks/DatabaseHealthCheck.php
class DatabaseHealthCheck implements HealthCheckInterface
{
protected $pdo;
public function __construct($host, $port, $name, $user, $pass)
{
// 注意:这里使用真实的短时连接,避免占用连接池
$this->pdo = new PDO("mysql:host=$host;port=$port;dbname=$name", $user, $pass, [
PDO::ATTR_TIMEOUT => 2 // 设置2秒超时
]);
}
public function check(): array
{
$start = microtime(true);
try {
$this->pdo->query('SELECT 1');
return [
'status' => 'ok',
'details' => 'Connection successful ('. (microtime(true) - $start) .'s)'
];
} catch (\Throwable $e) {
return ['status' => 'error', 'details' => $e->getMessage()];
}
}
}
示例B:Redis / 缓存检查
<?php
class RedisHealthCheck implements HealthCheckInterface
{
protected $redis;
public function __construct(\Redis $redis)
{
$this->redis = $redis;
}
public function check(): array
{
try {
$this->redis->ping(); // PING命令
return ['status' => 'ok', 'details' => 'PING success'];
} catch (\Throwable $e) {
return ['status' => 'error', 'details' => $e->getMessage()];
}
}
}
步骤 3:核心调度器(汇总 + 状态码判断)
<?php
// app/HealthCheck/Manager.php
class HealthCheckManager
{
protected $checks = [];
protected $criticalChecks = ['database', 'redis']; // 哪些是必须健康的?
public function addCheck(string $name, HealthCheckInterface $check, bool $isCritical = true)
{
$this->checks[$name] = ['check' => $check, 'critical' => $isCritical];
return $this;
}
public function diagnose(string $type = 'readiness'): array
{
$results = [];
$overallStatus = 'ok';
foreach ($this->checks as $name => $item) {
// 对于Liveness检查,只检查主进程
if ($type === 'liveness' && $name !== 'app_status') {
continue;
}
$result = $item['check']->check();
$results[$name] = $result;
// 关键检查失败 -> 整体状态设为error
if ($result['status'] === 'error' && $item['critical']) {
$overallStatus = 'error';
}
}
return [
'status' => $overallStatus,
'checks' => $results,
'timestamp' => time(),
'uptime' => (time() - $_SERVER['REQUEST_TIME_FLOAT'])
];
}
}
步骤 4:统一入口 Controller / Router
<?php
// routes/web.php (Laravel) 或 自定义路由
Route::get('/health/liveness', function () {
// 简化版:只要PHP进程活着,就返回200
return response()->json(['status' => 'ok']);
});
Route::get('/health/readiness', function () {
$manager = new HealthCheckManager();
// 注册依赖
$manager->addCheck('database', new DatabaseHealthCheck(env('DB_HOST'), env('DB_PORT'), env('DB_DATABASE'), env('DB_USERNAME'), env('DB_PASSWORD')));
$manager->addCheck('redis', new RedisHealthCheck(Redis::connection()->client()));
// 执行诊断
$result = $manager->diagnose('readiness');
// 关键:根据状态返回HTTP码
$statusCode = ($result['status'] === 'ok') ? 200 : 503;
return response()->json($result, $statusCode);
});
线上生产环境的注意事项
两个必做
- 禁用缓存:健康检查接口必须跳过所有业务缓存(包括OPcache、响应缓存),确保结果是实时的。
- 超时控制:所有连接必须设置
connect_timeout(建议 2-3秒),防止某个依赖服务“假死”导致健康检查接口本身也超时挂起。
三种特殊场景
- 数据库连接池:如果你的项目用了连接池(如Swoole),健康检查不要拿连接池里的连接去
SELECT 1,因为可能会取到已断连的旧连接,应该新建一个临时连接进行测试,用完立即销毁。 - 负载均衡器:如果有多台服务器,确保每台机器的健康检查都有独立的随机数参数(如
?ts=1641024000),避免CDN缓存返回旧结果。 - 只读端点:健康检查的URL应该完全公开,不要求JWT或Session认证,确保运维工具(如Prometheus、K8s探针)能直接访问。
日志记录
在健康检查失败时,不要将大量异常堆栈打印到应用日志(避免攻击者利用/日志爆炸),只记录错误码和简要信息:
// 在Manager diagnose方法中
if ($result['status'] === 'error') {
\Log::warning('Health Check Failed', [
'service' => $name,
'error' => (substr($result['details'], 0, 40)) // 截取前40字符
]);
}
进阶场景:深度业务检查(Deep Health)
当你的应用依赖大量中间件(消息队列、第三方支付)时,只查数据库往往不够。
示例:检查消息队列堆积是否超标
class QueueHealthCheck implements HealthCheckInterface
{
protected $redis;
public function check(): array
{
$queueLength = $this->redis->lLen('jobs_queue');
// 如果队列超过10000条,则视为“亚健康”
if ($queueLength > 10000) {
return [
'status' => 'error',
'details' => "Queue too long: {$queueLength}"
];
}
return ['status' => 'ok', 'details' => "Queue length: {$queueLength}"];
}
}
监控集成建议
如果你的健康检查接口已经返回JSON,推荐用以下工具采集:
- Kubernetes (K8s):配置
livenessProbe指向/health/liveness,readinessProbe指向/health/readiness。 - Prometheus:使用
php_exporter结合curl检查/health/readiness的 HTTP 状态码。 - 阿里云/腾讯云 SLB:配置 TCP 监听检查 + HTTP 请求检查。
完整代码示例(原生PHP,无框架)
如果你的项目没有框架,这个文件可以单独放在 public/ 目录下:
<?php
// health.php
header('Content-Type: application/json');
$response = ['status' => 'ok', 'timestamp' => time()];
// 1. 终极简单检查(Liveness)
if (php_sapi_name() === 'cli') {
// 命令行模式下无HTTP响应,直接退出
exit(0);
}
// 2. 检查数据库(Readiness)
try {
$pdo = new PDO('mysql:host=127.0.0.1;port=3306;dbname=app', 'user', 'pass', [
PDO::ATTR_TIMEOUT => 2
]);
$pdo->query('SELECT 1');
$response['checks']['database'] = 'ok';
} catch (\Exception $e) {
$response['status'] = 'error';
$response['checks']['database'] = $e->getMessage();
}
// 3. 检查磁盘写权限(对临时目录)
$testFile = sys_get_temp_dir() . '/health_' . uniqid() . '.txt';
if (@file_put_contents($testFile, 'test') === false) {
$response['status'] = 'error';
$response['checks']['disk'] = 'not writable';
} else {
unlink($testFile);
$response['checks']['disk'] = 'ok';
}
// 输出状态码
if ($response['status'] === 'error') {
http_response_code(503);
}
echo json_encode($response);
总结建议
- 小项目:用脚本检测核心依赖连接(基础方案即可)。
- 中大型项目:按 Liveness / Readiness / Deep Health 分层设计接口。
- 避免过度复杂:健康检查本身不应消耗过多资源,超时设置要短(3秒内),频率要合理。
建议你的健康检查接口返回结果包含 version 和 commit_hash,方便排查“哪次发布导致不健康”,这在排查问题时会很有帮助。