Consul服务注册总显示critical的根本原因是健康检查配置失配:/health接口返回非2xx、超时时间小于实际耗时、未设置DeregisterCriticalServiceAfter导致网络抖动即下线。

Consul 和 etcd 都能跑通服务发现,但选错一个,上线后就容易出现“服务列表有,连不上”“实例刚注册就消失”“健康状态一直 critical”这类问题。根本原因不是 SDK 没调对,而是注册模型、心跳机制、客户端行为这三块没对齐。
Consul 注册为什么总卡在 critical 状态
Consul 的 AgentServiceRegistration 是声明式模型:你注册时就得把健康检查逻辑(比如 /health 地址、Interval、Timeout)一并交出去,后续由 Consul 自己去轮询。常见错误是:
-
/healthhandler 返回非 2xx(比如返回 503 但没设FailuresBeforeCritical),Consul 直接标为critical -
Timeout设成 1s,但 handler 实际耗时 1.2s → 每次检查都超时,状态反复 flip - 忘了配
DeregisterCriticalServiceAfter,默认 1m,网络抖动一次就触发下线
建议:把 Timeout 设为 handler 耗时的 2 倍,Interval ≥ Timeout × 2;DeregisterCriticalServiceAfter 至少设为 30s。
etcd 注册后服务很快消失的真正原因
etcd 不管健康检查,只认租约(lease)。你必须自己做三件事:调 clientv3.Lease.Grant 拿 lease ID,用 clientv3.WithLease 把服务路径绑定到该 lease,再起 goroutine 调 Lease.KeepAlive 续租。漏掉任意一步都会导致 key 过期被删。
立即学习“go语言免费学习笔记(深入)”;
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
- 常见错误:
KeepAlivegoroutine 启动后,传进去的context被上层 cancel → 通道关闭,续租停止 - 租约 TTL 设太短(比如 5s),而客户端续租间隔是 3s → 网络延迟稍高就断租
- 没处理
KeepAlive返回的<chan></chan>,导致响应积压阻塞
建议:TTL ≥ 10s,续租间隔 ≤ TTL/2;用 context.WithCancel 单独管理续租生命周期;读取 KeepAlive 响应通道时加超时或 select default 防卡死。
gRPC 客户端对接服务发现的硬性条件
gRPC 不自动感知注册中心变化,必须靠自定义 resolver.Builder。光实现 Build 方法不够,以下三点缺一不可:
- 在
Build返回的resolver.Resolver中,必须启动Watch机制监听注册中心变更(比如 etcd 的clientv3.Watch或 Consul 的catalog.Servicelong poll) -
UpdateState推送的resolver.State.Addresses里不能有重复 IP+端口,否则 gRPC 轮询策略会当成多个 endpoint 连接,引发连接风暴 - 初始化
grpc.Dial时必须显式传grpc.WithResolvers,否则你的 resolver 根本不会被加载
性能提示:频繁全量推送地址列表会让 gRPC 内部重建连接,CPU 毛刺明显。建议只推送 diff,或加一层本地缓存 + 时间窗口节流。
Kubernetes 环境下还连 Consul/etcd 就多此一举
如果你的服务跑在 K8s 里,硬编码 consul.NewClient 或 clientv3.New 去连外部注册中心,等于绕过 K8s 原生能力。K8s Service DNS(如 my-svc.default.svc.cluster.local)已内置服务发现、健康探针、滚动更新和负载均衡——这些功能不需要额外 SDK,也不依赖第三方注册中心。
例外情况只有两个:跨集群调用、或需要与 K8s 外部系统(比如遗留的物理机服务)互通。其他所有场景,优先走 Service DNS + Headless Service + EndpointSlice,比自己维护一套注册逻辑更轻、更稳、更符合云原生范式。

















