Consul服务状态为critical的根本原因是健康检查未配置,需在ServiceRegistration中显式设置Check字段并确保HTTP探针端点返回2xx;etcd服务消失主因是租约未续期,须用KeepAlive()维持lease;gRPC需手写resolver实现服务发现。

Consul注册后服务状态一直是critical,怎么 fix?
根本原因不是注册失败,而是健康检查没跑通。Consul默认把没配检查的服务标为critical,哪怕服务本身已启动。
- 必须在
api.AgentServiceRegistration里显式传Check字段,不能只调ServiceRegister() - 推荐用HTTP探针:
HTTP: "http://localhost:8080/health",确保该端点返回2xx且耗时 ≤Timeout(建议设为5s) -
DeregisterCriticalServiceAfter别设太短,默认"1m"合理;设成"30s"容易因网络抖动误删实例 - 注册逻辑不能放在
init()里——此时HTTP server大概率还没Listen(),健康检查立刻失败
etcd注册后服务秒消失,是不是租约没续上?
90%是KeepAlive()漏了或者被提前关闭。etcd没有“自动心跳”,全靠客户端自己维持lease。
- 注册时必须用
clientv3.WithLease(leaseID)绑定key,否则写入即失效 - 拿到
leaseID后,得另起goroutine调Lease.KeepAlive(),且不能让其context被cancel - lease TTL建议 ≥
10s,续租间隔 ≤TTL/2(比如TTL=15s,每7s续一次) - Watch目录变化时,要自己解析
kv.Event.Type == mvccpb.PUT/DELETE,不能只收通道不判类型
gRPC客户端怎么拿到最新服务列表?
gRPC不内置服务发现,必须实现resolver.Builder,否则cc.UpdateState()永远不会触发。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
Build()方法返回的resolver.Resolver里,需在ResolveNow()中拉取当前服务列表 - 拿到新地址后,必须调
cc.UpdateState(resolver.State{Addresses: addrs})推送更新 - 别依赖轮询:gRPC会缓存结果,除非显式调
ResolveNow()或配置grpc.WithResolvers() - Kratos等框架已封装好这一层,但纯
grpc-go项目必须手写resolver
选Consul还是etcd?看这三点就够了
不是看谁更“流行”,而是看你的服务是否需要开箱即用的健康检查、DNS集成,以及你能否承担额外的客户端逻辑成本。
立即学习“go语言免费学习笔记(深入)”;
- 要HTTP/DNS多协议查询、自动健康检查、跨数据中心支持 → 选
Consul,Go客户端成熟,github.com/hashicorp/consul/api够用 - 只做轻量KV协调、已跑在K8s里、对CP强一致有硬要求 → 选
etcd,但得自己写租约续期、Watch解析、健康端点暴露 - 团队主栈是Java+Spring Cloud Alibaba → 考虑
Nacos,但Go SDK v2文档极差,GroupName和namespace必须手动设,否则服务混在一起
真正难的不是选哪个,而是注册中心与服务生命周期的对齐:服务启动时注册,退出前必须ServiceDeregister(),否则残留“僵尸服务”——这点在所有方案里都一样,但最容易被忽略。

















