consul注册后服务状态为critical,最常见原因是健康检查失败:check.http返回非2xx状态码或check.timeout设置过短;需curl验证地址响应及耗时,检查api返回的failure reason,并调大deregistercriticalserviceafter至90s以上。

Consul注册后服务状态一直是critical,怎么查?
不是注册失败,而是健康检查被判定为不通过。最常见原因是check.HTTP返回非 2xx 状态码,或check.Timeout设得太短(比如设成 "1s",但实际 /health handler 耗时 1.2s)。
- 先 curl 一下你填在
check.HTTP里的地址,确认返回 200 且响应时间 ≤ Timeout 值 - 检查 Consul UI 或调用
/v1/health/checks/{service-id}API,看具体 failure reason 字段 -
DeregisterCriticalServiceAfter默认是"1m",网络抖动时容易误删;生产环境建议调大到"90s"或更长
etcd注册后服务很快消失,是不是代码漏了什么?
大概率是租约没续上。etcd 不像 Consul 那样自动拉健康检查,它靠客户端自己维持 lease 生命周期。
- 必须显式调用
clientv3.Lease.KeepAlive并持续读取返回的KeepAliveResponsechannel,否则 lease 到期后 key 自动删除 - 不能用
context.WithCancel包裹 KeepAlive 的 context —— 一旦 cancel,channel 关闭,续租就停了 - lease TTL 建议 ≥
10s,续租 goroutine 的 sleep 间隔要 ≤ TTL/2(比如 TTL=10s,sleep 就别设成 6s)
gRPC 客户端连不上服务发现结果,resolver 写对了吗?
gRPC 不会自动触发 resolver,也不会默认监听变化。Build 和 Watch 是两个强依赖环节。
- 没实现
Watch()方法 → 只能解析一次,后续实例增减完全无感知 - 没在
grpc.WithResolvers()里注册你的resolver.Builder→grpc.Dial("my-service:///")根本不会走你的逻辑 - 直接把全量地址塞进
resolver.State.Addresses→ gRPC 轮询策略会对相同 IP+Port 去重,但如果你推送了 10 个重复地址,它仍会尝试建 10 条连接,引发连接风暴
K8s 里还硬连 Consul/etcd,有必要吗?
基本没必要。Kubernetes Service DNS 已覆盖绝大多数场景,且更轻量、更稳定。
- Service 名如
user-svc.default.svc.cluster.local天然支持 DNS 轮询和 endpoint 自动同步 - 硬连外部注册中心不仅增加运维负担,还会引入额外故障点(比如 Consul 网络不通导致整个服务不可用)
- 真有跨集群或混合云需求,优先考虑 K8s Federation 或服务网格(如 Istio 的 east-west gateway),而不是让每个 Go 服务自己连 Consul
服务发现真正的复杂点不在“怎么注册”,而在“什么时候该刷新”“刷新时要不要节流”“失效转移是否平滑”。这些细节不写进日志、不压测就很难暴露,上线后才出问题最麻烦。











