gRPC Go 客户端默认不启用负载均衡,需显式配置 resolver 与 balancer 协同工作;逗号分隔地址无效,必须用 scheme(如 dns:///)并注册 resolver.Builder;round_robin 需通过 WithDefaultServiceConfig 启用,且依赖 resolver 正确调用 UpdateState。

gRPC Go 客户端默认不启用任何负载均衡,传入多个地址也不会自动分发请求——必须显式配置 resolver + balancer 协同工作,否则 grpc.Dial 仅连接第一个地址或直接报错。
为什么 grpc.Dial("10.0.1.1:8080,10.0.1.2:8080") 不生效
gRPC 不解析逗号分隔的字符串;它把整个字符串当做一个目标 host 处理,结果是 DNS 解析失败或只用首个合法部分。真正支持多地址的前提是:使用 scheme(如 "dns:///myservice" 或自定义 "etcd:///user"),并注册对应 resolver.Builder。
- 内置
dnsresolver 支持 SRV 记录,适合 Kubernetes Headless Service + CoreDNS 场景 - 静态地址列表必须封装进自定义 resolver,不能靠拼接字符串“蒙混过关”
- 若未注册 resolver,即使写了
"demo:///foo",grpc.Dial会 panic 报 “no resolver found for scheme”
grpc.WithBalancerName("round_robin") 为何没反应
这个选项在较新版本(v1.27+)中已被弃用,且单独使用无效:balancer 必须配合 resolver 返回的地址列表才能激活。真正起效的是 grpc.WithDefaultServiceConfig 配置 JSON 字符串,或者确保 resolver 实现中调用了 cc.UpdateState 触发状态更新。
- 正确写法:
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`) - 如果用了自定义 resolver,但
UpdateState传入空Addresses或含非法格式(如Addr: ""),round_robin会静默跳过所有地址 -
pick_first是默认策略,不指定 config 就永远只连第一个
自己写 balancer 容易漏掉的关键点
直接实现 balancer.Balancer 接口看似简单,但生产环境极易出问题:地址变更不感知、Pick 阻塞、SubConn 状态不同步都会导致请求卡死或路由失效。
立即学习“go语言免费学习笔记(深入)”;
- 必须实现
UpdateClientConnState,否则服务实例上下线时客户端完全无感 -
Pick方法必须快速返回,禁止做网络 I/O、锁等待或耗时计算 - 不要手动管理
SubConn的创建/关闭——应继承base.Balancer,它已封装连接生命周期与重连逻辑 - 健康检查需独立于 balancer:gRPC 不自动探测后端存活,得靠
health check协议或外部心跳标记节点状态
最常被忽略的是 resolver 和 balancer 的耦合时机:resolver 返回地址后,balancer 并不会立刻生效,必须等 gRPC 内部完成 SubConn 初始化并报告 READY 状态。如果后端启动慢、TLS 握手卡住、或防火墙拦截,你看到的现象可能是“轮询没开始”,实际是连接还没建好。


















