consul服务注册必须在iris.run()前完成,因run会阻塞主线程;需在iris.new()后、run()前注册,确保consul客户端已就绪并连通,且用ttl健康检查配合独立goroutine心跳,注销则须在优雅关机时同步执行。

Consul服务注册必须在Iris启动前完成
Iris本身不内置服务发现逻辑,iris.Run() 会阻塞主线程,如果等它启动后再调用Consul注册,服务就已不可达。必须把注册逻辑放在iris.New()之后、iris.Run()之前,且需确保Consul客户端已就绪并能连通。
常见错误是把注册写在iris.ConfigureHost或iris.OnAny里——这些钩子都在服务监听启动后才触发,注册时服务端口虽已监听,但Consul可能因超时或重试机制判定为“未就绪”。
- 用
consulapi.NewClient()初始化客户端,显式设置Address和Timeout(建议≤5s) - 注册前手动调用
client.Status().Leader()或简单HTTP GET/v1/status/leader验证连通性 - 注册参数中
Name不要含下划线(Consul对服务名校验严格),Address填容器内可解析的IP(如Docker网络中用host.docker.internal或宿主机IP) - 务必设置
Check.TTL并配套启动定时心跳(见下节),否则Consul默认认为服务“不健康”
健康检查必须用TTL模式并独立维护心跳
Consul的HTTP健康检查(http类型)在Iris场景下容易失效:Iris默认不暴露/health端点,且HTTP检查依赖服务响应200,而开发期路由未就绪、中间件拦截或panic都可能导致误判。更可靠的是用TTL模式——服务主动上报“我还活着”,由Consul倒计时判断。
关键点在于心跳不能依赖Iris的HTTP handler,而要另起goroutine定时调用client.Health().UpdateTTL()。否则一旦Iris请求处理卡住(如DB连接池耗尽),心跳发不出,Consul立刻剔除服务。
- 注册时
Check结构体设TTL: "30s",对应心跳间隔必须≤该值(建议设为25s) - 心跳代码应捕获
consulapi.StatusCodeError,失败时不panic,仅记录日志并重试(网络抖动常见) - 心跳goroutine需监听
iris.Server().CloseNotify()或自定义关闭信号,避免进程退出后goroutine泄漏 - 不要复用注册时的
consulapi.Client实例做长连接——它内部有连接池,但TTL更新本质是HTTP PUT,无需特殊配置
服务注销要在Iris优雅关机时触发
直接kill进程会导致Consul里服务残留(状态变critical但不自动删除),下次启动若用相同ID会冲突。必须在Iris收到SIGINT/SIGTERM后、真正关闭监听端口前,调用Consul的Deregister()。
Iris 12+ 支持iris.WithoutInterruptHandler,此时需自己注册os.Signal;Iris 11及以下则依赖iris.RegisterOnInterrupt()。无论哪种,注销逻辑必须同步执行,且要容忍Consul临时不可达(加retry,最多2次)。
-
Deregister()传入的ServiceID必须与注册时完全一致(大小写、符号) - 注销前可先调用
client.Agent().PassingServiceTTL()快速标记服务为“即将下线”,降低流量打入概率 - 若使用Docker,确保容器
stop_signal设为SIGINT而非默认SIGKILL,否则无法捕获
环境变量驱动配置比硬编码更安全
Consul地址、服务名、端口等参数若写死在代码里,测试/生产环境切换就得改代码、重新编译。应该全量提取为环境变量,用os.Getenv()读取,并设合理默认值(如CONSUL_ADDR=127.0.0.1:8500)。
特别注意Service.Port字段:Iris启动时用:8080这种格式,但Consul注册需要纯数字端口(8080)。别用strings.Split(iris.Addr(), ":")[1]——本地开发可能是:0(随机端口),得从iris.Server().Addr反射获取实际绑定端口。
- 用
net.Listen("tcp", ":0")预占端口再传给Iris,这样启动前就能确定端口号 -
CONSUL_TOKEN若存在,必须通过consulapi.Config.Token传入,不能拼在URL里(Consul 1.10+拒绝token in URL) - 服务标签(
Tags)建议加version和env,如["v1.2.0", "staging"],便于后续灰度路由
最易被忽略的是Consul ACL策略:即使开了ACL,也要确认Token有service:write权限,否则注册静默失败(返回403但consulapi库默认不抛异常,只返回nil错误)。











