健康检查接口必须区分liveness与readiness:liveness仅验证进程http响应能力,readiness须同步检查主从库连通性及写能力,显式返回200或503状态码,并用select 1或轻量写操作验证连接真实性。

健康检查接口不能只返回 200 就算完事,ThinkPHP 默认不区分主从、不校验连接真实性、不捕获连接建立阶段异常——线上出问题时,这个接口往往还在“假装健康”。
如何写一个真正有用的 /health 接口
别用 Db::connect() 后直接 return ['status' => 'ok']。它连 TCP 都没发出去,配置写错都检测不出来。
- 必须主动触发一次轻量查询,比如
Db::query('SELECT 1')或Db::table('user')->count()(后者更贴近真实连接路径) - 读写分离场景下,要分别测主库和从库:
Db::master()->query('SELECT 1')和Db::slave()->query('SELECT 1') - 捕获两类异常:
PDOException(连接层失败,如SQLSTATE[HY000] [2002])和 ThinkPHP 的DbException(SQL 执行失败) - 响应体建议带关键字段:
status、db_master、db_slave、latency_ms,方便监控系统聚合
为什么不能依赖 $db->ping() 做判断
$db->ping() 在 ThinkPHP 6.1+ 的 Connection 对象里存在,但它底层行为严重依赖驱动:
- MySQLi 驱动下可用,调用的是
mysqli_ping(),能真实反映连接状态 - PDO 驱动下多数情况直接返回
true,因为 PDO 没有标准的 ping 接口,TP 实际是 fallback 到getAttribute(PDO::ATTR_CONNECTION_STATUS),而该属性在 MySQL PDO 中不可靠 - Unix socket 或 Swoole 协程环境里,
ping()可能永远返回true,但后续SELECT仍报MySQL server has gone away
稳妥做法:先 ping(),再立刻 query('SELECT 1'),两者都成功才算连接健康。
CLI 定时任务里做健康检查的坑
在 php think timer 或自定义命令中反复调用 Db::connect(),会导致连接泄漏:
- 每次
Db::connect()都新建Connection实例,PHP-FPM 或 CLI 进程不会自动释放 - MySQL 出现
Too many connections错误前,可能已堆积几十个空闲连接 - 正确做法是复用默认连接:
Db::name('user')->count()自动走连接池;或手动获取后显式关闭(仅 MySQLi 支持$db->close()) - CLI 场景建议数据库配置加
'break_reconnect' => true,避免复用僵死连接
Kubernetes Readiness 探针怎么配才不误伤
别把 /health 接口直接塞进 readinessProbe 的 httpGet 就完事。K8s 默认只看 HTTP 状态码,而 TP 健康接口常因主库挂掉却仍返回 200(只查了从库):
- Readiness 探针应要求「主库可写」:接口里必须执行
Db::master()->insert(['id' => uniqid()])+delete清理,否则流量导进来就写失败 - 避免高频探测:设
periodSeconds: 15,太短会压垮 DB;timeoutSeconds: 3,防止慢查询拖住探针 - 不要用
exec方式调curl,容器里未必装 curl;统一走 HTTP 探针,路径用/health?mode=ready区分语义 - 如果用了 Swoole,确保探针请求走的是 HTTP Server 而非 CLI 模式,否则
Db连接逻辑不一致
最易被忽略的一点:健康检查结果必须包含主库写能力验证,而不是“能连上就算活”。很多故障都是接口返回 200,但所有 POST 请求全 500 —— 因为主库早就断开了。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











