service.discover() 返回空列表是常态,非 bug;原因包括注册中心同步延迟、目标服务无健康实例或acl权限未生效,硬编码 panic 或固定重试会拖垮调用链。

service.Discover() 返回空列表不是 bug,是常态——注册中心同步延迟、目标服务暂无健康实例、ACL 权限未生效都可能导致这个结果。硬编码 panic 或重试 3 次就退出,会直接拖垮调用链。
Consul 注册必须等 gRPC 和健康检查就绪后才执行
很多服务一启动就调用 client.Agent().ServiceRegister(),但此时 grpc.Server 还没监听端口、/health 接口也没跑起来,其他服务一查到就立刻发请求,必然失败。
- 用
net.Listener.Addr()确认 gRPC 已绑定端口后再注册 - 对 Consul,注册前先发一次
PUT /v1/agent/check/pass/service:xxx,确保初始状态为passing -
DeregisterCriticalServiceAfter别设太短(默认"1m"),网络抖动容易误删;建议 ≥ 30s - 健康检查的
Timeout必须大于 handler 实际耗时,否则状态总卡在critical
etcd 注册必须绑定 lease 并 keepalive
etcd 是纯 KV + 租约模型,不续租就自动过期。常见错误是只写键不绑 lease,或 goroutine 启动后 context 被 cancel 导致 Lease.KeepAlive 通道关闭。
评估 Kubernetes 集群安全态势,覆盖 RBAC、工作负载安全、网络策略、基础设施即代码(IaC)、运行时监控和密钥管理等 30 项控制项……
- 先调
clientv3.Lease.Grant(ctx, ttl)获取 lease ID - 再用
clientv3.WithLease(leaseID)写入服务路径,如/services/user/10.0.1.5:8080 - 另起 goroutine 调
Lease.KeepAlive,间隔 ≤ TTL/2(TTL 建议 ≥ 10s) - 务必监听
keepAliveChan关闭信号,主动清理本地缓存
gRPC resolver 必须实现 Watch 和 ResolveNow
gRPC 默认 resolver 不支持动态更新,只做一次性解析。没 Watch 就无法感知实例增减,没 ResolveNow 就无法在连接断开时主动重拉。
-
Build方法返回的resolver.Resolver必须启动 goroutine 监听注册中心变更(etcd 用clientv3.Watcher,Consul 用 blocking query + index 维护) -
ResolveNow里触发一次全量拉取,再调cc.UpdateState()推送新地址 - 别把重复地址塞进
resolver.State.Addresses,gRPC round_robin 会当成多个 endpoint,引发连接风暴 - 初始化
grpc.ClientConn时必须传grpc.WithResolvers(),否则自定义 resolver 根本不生效
Kubernetes 环境下别自己连 Consul/etcd
如果你的服务跑在 K8s 里,硬编码 consul.NewClient() 或 clientv3.New() 连外部注册中心,属于冗余架构。K8s Service DNS(如 user-svc.default.svc.cluster.local)已天然支持服务发现和负载均衡。
- 客户端直接用
dns:///user-svc.default.svc.cluster.local作为 target - 确保 Service 类型为
Headless(clusterIP: None),才能解析出全部 Pod A 记录 - gRPC 自动启用
round_robin,无需 resolver 或 balancer - 健康剔除由 K8s readiness probe 控制,不用额外实现
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










