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

Consul 是 Go 微服务里最常落地的服务发现方案,不是因为它“高级”,而是它能把健康检查、注册、查询这三件事串成一条不掉链子的流水线。只要配置对了,服务挂了自动剔除,新实例上线秒级可见——前提是别踩几个关键坑。
注册时漏掉 Check 字段,实例永远是 critical
服务能注册成功,不代表它真能被调用。Consul 默认把没配健康检查的服务标记为 critical,查的时候会被过滤掉(除非你主动关掉健康状态校验)。这不是 bug,是设计:它强制你声明“怎么判断我活着”。
-
AgentServiceRegistration.Check必须非 nil,哪怕只是个TTL类型也要显式设初值 - HTTP 检查最常用:
HTTP: "http://localhost:8080/health"+Interval: "10s"+Timeout: "3s" - 别用
localhost当Address—— 容器或跨主机部署时,其他服务根本连不上;得填容器网络可路由的真实 IP 或 DNS 名 - 服务
ID必须全局唯一,重启服务时若 ID 不变,旧实例注销失败会导致“幽灵节点”
查服务不加 PassingOnly: true,拿到的是脏数据
api.Health().Service() 默认返回所有状态的节点,包括 critical、warning 甚至已下线但未清理的残留条目。生产环境必须加过滤。
- 调用时传
&api.QueryOptions{AllowStale: false, PassingOnly: true} -
AllowStale: false避免读到过期缓存(Consul 默认允许 stale read 提升吞吐) - 别依赖单次
Get结果做长期决策——网络抖动可能让这次查询失败或超时,应结合本地缓存 + 定期刷新
kv.Get 返回 nil 不报错?那是 key 不存在或权限不够
Consul KV 的错误处理很反直觉:kv.Get("missing-key", nil) 的 error 是 nil,真正该判空的是返回的 *api.KVPair。
- 必须检查
pair != nil,否则pair.Valuepanic -
pair == nil可能是 key 不存在、ACL 拒绝、路径是个目录,三者表现一致 - 客户端初始化时别用
consul.DefaultConfig()—— 它的HttpClient是 nil,会 fallback 到全局http.DefaultClient,没超时,卡死不报错 - 显式设超时:
cfg.HttpClient = &http.Client{Timeout: 3 * time.Second}
Watch 不是长连接,轮询逻辑得自己兜底
Consul Go SDK 的 kv.Watch 是 HTTP 轮询封装,不是 WebSocket。直接 for 循环调用等于高频刷接口,还容易漏事件。
- 用
watch.NewWatcher替代裸kv.Watch,它内置指数退避和重连 - Watcher 的
ctx必须可控(比如随服务生命周期 cancel),否则 goroutine 泄漏 - ACL 启用时,Watcher 必须带 token,且 token 至少有对应 key path 的
read权限 - 每次 Watch 返回的是完整值,不是 diff —— 业务层要自己比对是否真变化,避免无谓 reload
Consul 的“开箱即用”只在健康检查层面成立;注册、查询、KV 这三块的容错、超时、空值判断,全得你手动补全。最容易被忽略的,是那个看似无关紧要的 HttpClient 超时配置——它不设,整个服务发现链就卡在第一次配置拉取上,连日志都不报,只默默等 30 秒默认超时。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











