consul健康检查必须显式配置check类型(http/tcp/ttl),否则状态恒为critical;http检查需完整url且返回2xx,address不可用localhost,查询须加passingonly:true过滤。

Consul 健康检查不是“注册完就自动生效”,必须显式声明检查类型(HTTP、TCP、TTL之一),否则服务在 Consul UI 里永远显示 critical,下游根本发现不了你。
Consul 注册时漏掉 Check 字段,服务状态恒为 critical
很多 Go 服务启动后查不到实例,日志也没报错,其实只是注册结构体里 Checks 字段为空或只填了 Timeout/Interval。Consul 不会自动 fallback 到默认检查——它直接当“无健康检查”处理,状态锁死 critical。
- 错误写法:
Check: &api.AgentServiceCheck{Timeout: "5s", Interval: "10s"}—— 缺少HTTP或TCP字段,注册成功但不健康 - HTTP 服务必须用完整 URL:
HTTP: "http://127.0.0.1:8080/health",且后端必须返回 HTTP 2xx(404、503、302全算失败) - TCP 检查仅验证端口通不通,无法反映应用层状态(比如 DB 挂了但端口仍 open),慎用于核心服务
-
DeregisterCriticalServiceAfter: "90s"是兜底必需项,避免进程崩溃后“僵尸服务”长期残留
/health 接口返回 200 不代表服务真健康
硬编码 w.WriteHeader(200) 然后返回 {"status":"ok"} 是典型反模式。Consul 和 Kubernetes 只看状态码,不读 body;但人运维要靠 body 定位问题,所以得兼顾两者。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 必须并行检查关键依赖(DB、Redis、下游 gRPC),每项设独立超时(如
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)) - 任意依赖失败,就返回
http.StatusServiceUnavailable(503),不能还打 200 - 响应 body 里带上失败项,例如
{"db": "timeout", "redis": "ok", "auth-svc": "connection refused"},Consul 不解析它,但人一眼能看懂 - 别在
/health里做重载配置、刷缓存等副作用操作——它只该是只读探测
microg 框架里 Consul 健康检查容易静默失效
microg 默认不自动注册 /health endpoint,也不帮你配 Consul 的健康检查参数。看似调了 app.Run() 就完事,实则 Consul agent 启动顺序、client 初始化、健康检查开关全得手动对齐。
-
consul.New()构造时必须显式传consul.WithHealthCheck(true),否则即使注册了服务,Consul 也不会拉你的/health - Consul agent 必须先于服务启动,且地址默认是
127.0.0.1:8500;改地址不能只靠环境变量,得用api.DefaultConfig().Address显式覆盖 - microg 不重试 Consul 连接失败,默认直接退出;想容错,得自己包装
registrar加重试逻辑和超时控制 - 如果用了 TTL 类型(需主动上报),
PassTTL()必须用 goroutine 定时调,间隔建议设为 TTL 的一半(如 TTL=30s,则每 15s 调一次)
微服务治理中健康检查与熔断、链路追踪的边界要划清
健康检查只管“这个实例此刻能不能收请求”,它不负责“连续失败后拦住后续请求”(那是熔断器的事),也不管“trace_id 能不能透传到下游”(那是中间件注入的事)。混在一起搞,出问题时根本分不清是哪一层塌了。
-
rpcclient超时后返回错误 ≠ 触发熔断:microg 不内置熔断器,得集成sony/gobreaker,且每个下游服务实例要配独立CircuitBreaker实例 -
OpenTelemetrytraceID 在 HTTP → gRPC 链路中断,是因为 microg 不自动透传traceparentheader,得在 client 拦截器里手动注入 - zap 日志字段依赖上下文传递,如果
context.WithValue()没贯穿到底层 handler,日志里就看不到trace_id或service_name - 健康检查路径别和
/metrics、/debug/pprof绑同一个 mux —— pprof 必须鉴权,而健康检查要开放给 Consul agent,混在一起容易误开调试接口
真正难的不是写一个返回 200 的 /health,而是让 Consul 看得懂、Kubernetes 信得过、运维查得清、下游调得稳——每个环节的配置开关、超时设定、错误码语义都得对齐,漏一个,整条链路就断在看不见的地方。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










