Gin本身不处理服务注册,必须手动调用Consul API完成;注册时必须显式配置Check字段(如HTTP探针指向/health),否则状态为critical;查询服务须用Health().Service()并设置PassingOnly:true。

Gin 本身不处理服务注册,必须手动调用 Consul API 或借助 client 库完成注册;直接把 Gin Server 当作“服务实例”注册进去是常见误区。
Consul 注册必须显式传入 Check 字段,否则状态为 critical
Consul 不会自动为注册的服务做健康检查。如果你只写 service.Name 和 service.Address,注册成功后在 UI 里看到的状态永远是 critical,客户端查不到这个实例。
- 必须配置
Check字段,最常用的是 HTTP 类型:指定一个 Gin 路由(如/health),Consul 定期 GET 它 - 超时、间隔、失败阈值都要设合理值,例如:
Timeout: "2s"、Interval: "5s"、DeregisterCriticalServiceAfter: "30s" - 别漏掉
HTTP字段的完整 URL,比如"http://127.0.0.1:8000/health"—— 注意协议、IP、端口必须和实际监听一致
客户端查询服务时要加 PassingOnly: true
用 api.Health().Service() 查询服务,默认返回所有状态的实例(包括 critical、passing、warning)。不加过滤,你会拿到一堆已下线但没被及时清理的脏地址。
- 必须显式传
api.QueryOptions{AllowStale: false, PassingOnly: true} -
AllowStale: false避免读到过期缓存(尤其在多数据中心场景) - 返回结果里
HealthChecks是个 slice,要自己遍历取Service.Address和Service.Port
Gin 启动后才注册,且需监听退出信号做优雅注销
注册动作不能放在 main() 开头就执行——此时 Gin 还没监听端口,Consul 健康检查立刻失败;也不能不处理进程退出,否则 Consul 里残留 dead 实例。
- 注册逻辑应放在
router.Run()之前,但确保http.ListenAndServe已准备好(即 Gin 的Run()内部已绑定 socket) - 更稳妥做法:启动 goroutine 延迟 100ms 再注册,或监听
http.Server的Started信号(需自定义封装) - 用
os.Interrupt和syscall.SIGTERM捕获退出,调用client.Agent().ServiceDeregister()主动注销
最容易被忽略的一点:Consul 的服务名(Service.Name)必须全局唯一,且不能含下划线(_)——它会被 DNS 解析机制拒绝,后续用 service-name.service.consul 做服务发现时直接失败。


















