Go依赖健康检测必须带context.WithTimeout,禁用无超时的db.Ping();应使用后台goroutine定期探测并缓存结果,/readyz仅读快照;TCP直连比HTTP探针更准;Consul注册需显式配置health_check否则默认critical。
Go 依赖健康检测必须带 context.WithTimeout,不能裸调 Ping
不设超时的
或
在网络异常时可能卡住几十秒,直接拖垮整个健康检查流程。Kubernetes 默认探针超时是 1 秒,Consul 健康检查默认也只等几秒——超时即失败,失败即剔除或重启。
正确做法是:每个依赖探测都基于请求上下文(
)构造独立子 context,超时设为探针间隔的 60% 左右(比如 K8s 设置
,则代码里用
)。
是必须,
只校验 DSN,不建连
Redis 用
,别用
(只验配置)或
(带键操作非轻量)
HTTP 下游服务,用
+
,禁用
(它用全局 client,共享连接池和重试逻辑)
所有超时错误要明确识别:
、
、
都应归为依赖不可用
/readyz 必须缓存探测结果,禁止每次请求都真实拨号
高频探针(如每 2 秒一次)下,每次
handler 都执行
,会瞬间打爆数据库连接池,且让 K8s 因单次超时反复杀 Pod。这不是“检查”,是脉冲攻击。
真正可靠的模式是:启动一个后台 goroutine,用
定期(如每 5 秒)执行全量依赖探测,把结果写入线程安全结构体;
handler 只读快照,不触发新连接。
立即学习
“
go语言免费学习笔记(深入)
”;
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
缓存结构体至少含:
("UP"/"DEGRADED"/"DOWN")、
、各依赖明细(如
)
用
保护读写,避免 goroutine 竞态
不要在 handler 里做
或未 recover 的 panic——那等于主动退出进程
如果某次探测耗时过长(比如 Redis 响应慢),缓存上次成功结果,而不是让整个端点阻塞
TCP 层直连比 HTTP 探针更准,尤其对中间件穿透场景
HTTP 探针要走完整 TLS 握手 + HTTP 解析,容易被 Ingress、Nginx、Istio 等中间件拦截、限速或改写状态码;而 TCP 连通性检查只关心三次握手是否成功,延迟更低、路径更短、结果更真实。
对下游节点(如 MySQL 地址
、Redis
)做 TCP 心跳时,应启独立 goroutine,用
,ctx 超时设为 600ms(覆盖 DNS + connect 全链路),连上立刻
,不发任何数据。
失败不重试,但状态变更需去抖:连续 3 次失败且最近一次在 1 秒内才标 Down;恢复需连续 2 次成功且间隔 > 500ms 才标 Up
禁用
做心跳——它的连接池复用和重试机制会掩盖真实连通性问题
ICMP ping(如用
)可作为补充,但容器环境常因权限/SELinux 被禁,TCP 更通用
Consul 注册后显示 critical,90% 是 health_check 字段没配或配错
Consul 不是“注册即健康”,而是默认认为所有服务“不健康”,除非你显式声明
。没配
= 永远 critical,不是 bug,是设计。
最简但有效的配置是加一个 HTTP 探针:
,并确保该端点返回 200 + JSON body(如
)。别漏掉
,建议设为
,否则服务挂了 Consul 感知不到。
Consul Agent 必须本地运行,Client 初始化地址必须是
,不能填公网 IP 或 DNS 名
别指望 TCP 检查能代替业务健康:端口通 ≠ DB 可连 ≠ Redis 可写,它只能兜底防网络层断连
响应体里带明细字段(如
)对运维排查极有用,Consul 不消费它,但人需要
关键点始终是:依赖探测不是“有没有”,而是“能不能用”;缓存不是可选项,是必选项;语义分离(
vs
)一旦混淆,滚动更新就卡死。
db.Ping()client.Ping()r.Context()periodSeconds: 5context.WithTimeout(ctx, 3*time.Second)db.PingContext(ctx)sql.Openclient.Ping(ctx).Err()ConfigCheck()Get("health")http.NewRequestWithContext(ctx, ...)client.Do(req)http.Get"dial tcp: i/o timeout""connection refused""context deadline exceeded"/readyzdb.PingContexttime.Ticker/readyzstatustimestamp{"db": "OK", "redis": "failed"}sync.RWMutexlog.Fatal10.10.1.5:330610.10.1.6:6379net.DialContext(ctx, "tcp", addr)conn.Close()http.Clientgithub.com/tatsushid/go-fastpingCheckshealth_checkHTTP: "http://127.0.0.1:8080/readyz"{"status": "UP"}DeregisterCriticalServiceAfter"90s""http://127.0.0.1:8500""db": "failed"/livez/readyz