真正有用的健康接口需分通道检查主库、缓存、队列,均加try/catch与超时控制,组合ping()和SELECT 1验证连接有效性,返回结构化状态数据,且随架构演进持续维护。

系统监控接口不能只返回 200 就算健康,主库挂了、缓存连不上、队列堆积,接口照样能通——这种“假健康”在生产环境最危险。
如何写一个真正有用的 /api/health 接口
别只查数据库连通性,ThinkPHP 的读写分离会让 Db::connect() 默认走从库,而你真正怕的是主库不可写。必须分通道检查:
- 用
Db::connect('write')显式连主库,再执行Db::query('SELECT 1') - 用
Cache::store('redis')->getInfo()拿hits/misses,再Cache::get('health_test')写读一次 key - 检查队列连接:对 Redis 驱动调
queue()->connection()->getRedis()->ping(),再queue()->size('default')看积压量 - 所有检查必须加
try/catch,捕获PDOException、RedisException、ThinkPHP\exception\DbException
为什么 ping() + SELECT 1 是最低成本的健康判定
$db->ping() 在 PDO 驱动下基本没用,它不触发真实网络 I/O,常返回 true 却后续查询直接失败;MySQLi 下虽可用,但 wait_timeout 导致空闲连接断开后,ping() 仍可能返回 true。所以必须组合验证:
- 先
$db->ping()快速筛掉明显断连 - 再立刻执行
Db::query('SELECT 1'),确保连接可执行语句 - 两者都成功才标记该通道为
healthy: true - 超时统一设为
500ms,避免单点故障拖垮整个接口响应
CLI 定时任务里做健康检查的陷阱
在 php think timer 或自定义命令中反复调 Db::connect(),会不断新建 Connection 实例,而 CLI 模式下连接不会自动回收,极易触发 Too many connections。正确做法是:
立即学习“PHP免费学习笔记(深入)”;
- 改用
Db::name('user')->count()这类静态方法,复用默认连接池实例 - 若需手动管理,检查完立即调
$db->close()(仅 MySQLi 有效) - 数据库配置中启用
'break_reconnect' => true,让框架自动丢弃僵死连接 - 高频检查(如每 10 秒)务必加锁或限频,避免并发打爆 DB
监控接口返回结构该怎么设计
返回纯 200 + {"status":"ok"} 是反模式。前端、运维、告警系统都需要结构化数据:
- 每个依赖项单独字段:
"db_write"、"cache_redis"、"queue_default" - 每项含
status(up/down)、latency_ms、error(失败时填简短原因) - 顶层加
"overall"字段,仅当所有status === 'up'才为up,否则degraded或down - 不要记录详细堆栈,避免敏感信息泄露;错误原因用白名单枚举,如
"connection_refused"、"timeout"
真正难的不是写通这个接口,而是持续维护它的有效性——比如某天你加了个新缓存驱动,却忘了更新健康检查逻辑,它就悄悄变成“盲区”。每次架构变更,都要同步审视 /api/health 的覆盖范围。



















