Consul服务注册必须配置健康检查,否则崩溃的服务实例会“假在线”;需暴露/health端点并绑定HTTP检查,设置Interval(5–10s)、Timeout和DeregisterCriticalServiceAfter(推荐30s),且Gin监听端口须与Consul注册端口严格一致。

Consul服务注册必须带健康检查,否则实例会“假在线”
不配健康检查的 ServiceRegister 会让服务在 Consul 中长期挂着,哪怕进程已崩溃。Gin 启动后必须暴露一个可被 Consul 轮询的健康端点(比如 /health),并在注册时绑定 HTTP 或 TCP 检查。
常见错误是只注册服务、没起 HTTP 健康服务,或健康接口返回非 200 状态但没设 Timeout 和 DeregisterCriticalServiceAfter,导致故障实例无法自动下线。
-
Interval建议设为"5s"到"10s",太短增加 Consul 压力,太长故障感知延迟高 -
HTTP地址要写完整,例如"http://127.0.0.1:8080/health",不能只写路径 -
DeregisterCriticalServiceAfter必须设置,推荐"30s",避免网络抖动误注销
服务发现时别直接用 client.Agent().Services(),它返回全部服务
这个方法返回的是 Consul 中所有已注册服务的映射,不是当前服务要调用的目标列表。真正做服务发现,得用 client.Health().Service(),传入目标服务名(如 "user-service"),才能拿到健康实例列表。
容易踩的坑是把服务名写错(大小写敏感、带空格)、没过滤 Passing 状态,或者忽略 ServiceNode.Service.Port —— Gin 默认监听的端口不一定等于注册时填的端口,必须从返回结构里取真实端口。
立即学习“go语言免费学习笔记(深入)”;
- 调用前加
passingOnly := true参数,确保只取通过健康检查的节点 - 结果里每个
ServiceEntry的Service.Address可能为空,此时要用Node.Address - 别硬编码
127.0.0.1,生产环境服务可能跨主机,地址必须动态读取
Gin 路由和 Consul 注册端口不一致会导致调用失败
Gin 启动时用 r.Run(":8080"),但注册到 Consul 的 Port 写成 8081,下游服务按 Consul 返回的地址去连,必然 connection refused。两者必须严格一致,或统一用变量控制。
更稳妥的做法是启动 Gin 前先生成监听地址,再把该地址和端口同时用于 http.ListenAndServe 和 Consul 注册。别依赖 localhost 或 127.0.0.1,容器或 K8s 环境下实际 IP 往往是网桥地址。
- 用
net.ResolveTCPAddr("tcp", ":8080")获取实际监听地址,再提取Port - 注册时
registration.Address填宿主机可路由的 IP(如eth0绑定地址),不是0.0.0.0 - 本地开发可用
util.GetLocalIP(),但上线前必须替换为配置项或环境变量
goroutine 泄漏:服务注销没配 defer 或没处理 panic
服务退出时要调用 client.Agent().ServiceDeregister(),否则 Consul 里残留死实例。但很多人只在 main() 结尾调用,没考虑程序 panic 或 SIGTERM 信号中断场景。
正确做法是用 os.Interrupt 和 syscall.SIGTERM 监听退出信号,在 goroutine 里阻塞等待,收到信号后执行注销。同时确保注销逻辑本身不会 panic —— 比如 Consul 连接已断,ServiceDeregister 会报错,得 recover。
- 注销前加日志,确认执行到了这一步
- 别在 defer 里直接调
ServiceDeregister,因为 client 可能已被 close - 测试时手动 kill 进程,立刻查 Consul UI 看实例是否消失,这是最直接的验证方式


















