hyapp-server/docs/健康检查.md
2026-05-12 21:51:39 +08:00

366 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Health Check Development Guide
本文档只定义部署稳定性需要的健康检查能力。健康检查不承载业务判断,不替代监控告警,也不用于修复故障。进程下线和 drain 细节见 `docs/优雅停机.md`
## Goals
健康检查必须解决三个问题:
- 负载均衡是否可以把新流量打到这个进程。
- 发布或重启时,进程是否可以安全下线。
- 多机部署时,依赖异常是否会被及时摘流,而不是继续接收请求并放大故障。
健康检查不解决这些问题:
- 不判断房间是否存在。
- 不判断用户是否有权限。
- 不扫描全量 MySQL 表。
- 不做 Redis 修复、房间恢复、outbox 补偿。
- 不在检查路径里触发业务写入。
## Check Types
所有服务都必须区分 `live``ready`
| Type | Meaning | Failure Action |
| --- | --- | --- |
| `live` | 进程还活着,事件循环或 gRPC/HTTP runtime 未卡死 | 由容器平台重启进程 |
| `ready` | 当前进程可以接新流量 | 从负载均衡摘流,不一定重启 |
不要把下游短暂不可用放进 `live`。例如 MySQL 抖动时,`room-service` 应该 `ready=false`,而不是 `live=false` 导致所有副本被重启。
## Required Interfaces
### HTTP Services
HTTP 服务暴露两个只读接口:
```text
GET /healthz/live
GET /healthz/ready
```
适用服务:
- `gateway-service`
成功响应:
```json
{
"status": "ok",
"service": "gateway-service",
"node_id": "gateway-node-1",
"time_unix_ms": 1777000000000
}
```
失败响应:
```json
{
"status": "fail",
"service": "gateway-service",
"node_id": "gateway-node-1",
"time_unix_ms": 1777000000000,
"checks": [
{
"name": "room_grpc",
"status": "fail",
"message": "deadline exceeded"
}
]
}
```
状态码规则:
- `live` 成功返回 `200`
- `live` 失败返回 `500`
- `ready` 成功返回 `200`
- `ready` 失败返回 `503`
### gRPC Services
gRPC 服务必须注册标准 gRPC Health Checking Protocol
```protobuf
grpc.health.v1.Health
```
适用服务:
- `room-service`
- `wallet-service`
- `user-service`
- `activity-service`
服务名使用完整服务边界名:
```text
gateway-service
room-service
wallet-service
user-service
activity-service
```
状态映射:
| gRPC Health Status | Meaning |
| --- | --- |
| `SERVING` | ready |
| `NOT_SERVING` | not ready |
| `UNKNOWN` | 启动中或检查器未初始化 |
本地 Docker 可以用 `grpc-health-probe` 或服务内置 HTTP admin 口做容器 healthcheck。线上优先使用云负载均衡、Kubernetes 或服务发现系统原生的 gRPC health check。
## Service Readiness Rules
### gateway-service
`live`
- HTTP server runtime 正常。
- 进程没有进入不可恢复 panic 状态。
`ready`
- JWT 配置已加载。
- HTTP listener 已启动。
- `room-service` gRPC 地址可连接。
`gateway-service` 启动阶段会连接 public auth rate limit Redis连接失败时进程不进入服务状态。`ready` 不持续探测 MySQL、Redis、wallet也不主动探测腾讯云 IM 外网接口;运行期 auth rate Redis 异常由登录注册入口 fail-closed 返回上游错误。UserSig 配置缺失时由 `/api/v1/im/usersig` fail-closed不影响本地房间 HTTP 链路 ready。
### room-service
`live`
- gRPC server runtime 正常。
- Room manager 主循环未停止。
- Outbox worker 主循环未停止。
`ready`
- MySQL `PingContext` 在超时时间内成功。
- Redis `PING` 在超时时间内成功。
- 当前节点不处于 draining 状态。
- gRPC listener 已启动。
- Redis lease 操作能力正常,至少能执行一次轻量读写或 Lua 脚本探测。
- 节点启动时必须能写入 `room:node:{node_id}`;运行期心跳失败要记录告警,避免其他 room 节点无法转发到当前 owner。
`wallet-service` 建议作为 `SendGift` 链路的强依赖监控,但不要默认作为 `room-service` 整体 ready 的硬失败条件;否则 wallet 短暂异常会导致房间基础命令也被摘流。
腾讯云 IM 不作为 `room-service` ready 的硬依赖。`PublishRoomEvent` 同步失败后由 outbox 补投,不能因为外部 IM 抖动把房间状态服务整体摘掉。
### Tencent Cloud IM
腾讯云 IM 是外部托管依赖,不在本仓库提供 `live`/`ready` 接口。
本仓库只检查两类内部条件:
- `gateway-service` 缺少 `sdk_app_id``secret_key` 时,`/api/v1/im/usersig` 必须失败关闭,不能返回假票据。
- `room-service` 启用腾讯云 IM 桥接时必须加载 REST API 配置;缺少管理员账号或密钥时服务启动失败,避免创建房间后无法建立外部群组。
线上需要独立监控腾讯云 IM REST API 错误率、UserSig 签发失败率和房间系统消息补投积压,不把外网探测塞进每次 readiness。
### wallet-service
`live`
- gRPC server runtime 正常。
`ready`
- gRPC listener 已启动。
- 未来接入账务数据库后,账务数据库连接必须成功。
### user-service
`live`
- gRPC server runtime 正常。
`ready`
- gRPC listener 已启动。
- 未来接入用户数据库后,用户数据库连接必须成功。
### activity-service
`live`
- gRPC server runtime 正常。
- Consumer 主循环未停止。
`ready`
- gRPC listener 已启动。
- 未来接入 MQ/Redis Stream 后,消费位点存储可访问。
- 当前节点不处于 draining 状态。
## Timeout Budget
健康检查必须快失败,不能拖垮服务。
| Check | Timeout |
| --- | ---: |
| 本进程状态 | `50ms` |
| HTTP/gRPC listener 自检 | `100ms` |
| Redis `PING` | `200ms` |
| MySQL `PingContext` | `300ms` |
| 下游 gRPC health | `300ms` |
单次 `ready` 总耗时建议小于 `500ms`。如果检查项较多,使用并发检查并设置总 deadline。
## Docker Compose
本地 `docker-compose.yml` 应为每个服务配置 healthcheck。HTTP 服务使用 `curl`gRPC 服务使用 `grpc-health-probe` 或服务提供的 admin health endpoint。
HTTP 示例:
```yaml
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:13000/healthz/ready"]
interval: 5s
timeout: 3s
retries: 20
start_period: 10s
```
gRPC 示例:
```yaml
healthcheck:
test: ["CMD-SHELL", "grpc-health-probe -addr=127.0.0.1:13001 -service=room-service"]
interval: 5s
timeout: 3s
retries: 20
start_period: 10s
```
`depends_on.condition=service_healthy` 只用于本地编排启动顺序。线上不能依赖 compose 的启动顺序保证服务稳定,必须依赖负载均衡 readiness 和服务自身重试。
## Online Deployment
多机部署时,每个实例必须有唯一 `node_id`
推荐变量:
```text
SERVICE_NAME=room-service
NODE_ID=room-prod-az1-001
HEALTH_READY_TIMEOUT=500ms
DRAIN_TIMEOUT=30s
```
线上接入规则:
- SLB、Kubernetes Service、Nacos、Consul 只把 `ready=true` 的实例放进可用池。
- 发布新版本时先等待新实例 `ready=true`,再下线旧实例。
- 下线旧实例时先切 `ready=false`,再进入 drain最后退出进程。
- 不允许直接 `kill -9` 正常发布中的服务。
## Graceful Shutdown
所有服务收到 `SIGTERM` 后执行相同状态机:
```text
running -> draining -> stopped
```
`draining` 要求:
- `ready` 立即返回失败。
- 停止接收新连接或新请求。
- 等待当前请求完成,超过 deadline 后强制关闭。
- 关闭前写清必要的部署状态日志。
各服务额外要求:
| Service | Drain Requirement |
| --- | --- |
| `gateway-service` | 停止接新 HTTP 请求,等待已有请求结束 |
| `room-service` | 停止接管新房间,停止或释放 lease刷 snapshot暂停 outbox 当前批次后退出 |
| `wallet-service` | 等待当前扣费 RPC 完成,禁止中途截断 |
| `activity-service` | 完成当前消费消息或安全提交失败状态 |
客户端实时连接由腾讯云 IM SDK 承载,本仓库服务下线时不再发送 WebSocket close code。客户端收到腾讯云 SDK 断线事件后,按 SDK 指引重新登录并重新进群。
## Room Service Lease Safety
`room-service` 的健康检查必须和 Redis lease 下线策略配合。
发布或故障时:
1. 节点进入 draining。
2. `ready=false`,负载均衡不再打新请求。
3. 节点停止获取新的 `room:route:{room_id}` lease。
4. 已持有 Room Cell 刷 snapshot。
5. lease 释放或自然过期。
6. 新节点接到请求后通过 snapshot + command log 恢复。
禁止在 `ready=false` 之后继续接管新房间。否则节点下线期间会持续获得新 room lease导致退出时间不可控。
## Observability Fields
健康检查日志只在状态变化时输出,不能每 5 秒刷一条成功日志。
状态变化日志必须包含:
```text
service
node_id
old_status
new_status
failed_check
reason
duration_ms
```
建议指标:
```text
health_ready_status
health_live_status
health_check_duration_ms
health_dependency_failures_total
service_draining
service_shutdown_duration_ms
```
`room-service` 额外指标:
```text
room_lease_check_failures_total
room_snapshot_flush_failures_total
room_outbox_worker_alive
```
腾讯云 IM 相关指标不来自本地进程健康检查,单独采集 REST API 调用失败、UserSig 签发失败和 room outbox 补投延迟。
## Implementation Checklist
新增或修改服务时,必须检查:
- 是否提供 `live`
- 是否提供 `ready`
- `ready` 是否包含本服务真正的硬依赖。
- 下游依赖失败是否只影响 `ready`,没有误杀 `live`
- 检查是否有总 timeout。
- 检查路径是否没有业务写入。
- `SIGTERM` 后是否立即让 `ready=false`
- Dockerfile 是否包含 healthcheck 所需工具,或服务是否提供可被容器调用的 health endpoint。
- `docker-compose.yml` 是否配置 service healthcheck。
- 线上部署脚本是否等待新实例 ready 后再切流。
## Do Not
- 不要用业务接口当健康检查,例如 `CreateRoom``JoinRoom``SendGift`
- 不要在健康检查里创建房间、写 command log、写 outbox。
- 不要把所有下游都塞进每个服务的 ready。
- 不要把 Redis/MySQL 抖动映射成 `live=false`
- 不要让健康检查依赖外网。
- 不要把健康检查失败日志刷屏。
- 不要在滚动更新时跳过 drain。