grpc-go v1.27+需显式配置resolver和{"loadbalancingpolicy":"round_robin"}启用客户端负载均衡,禁用默认dns缓存,http轮询须自定义roundtripper并处理健康剔除、原子计数与连接复用。

Go 微服务里写负载均衡算法,90% 的场景都不该自己动手——基础设施(如 Envoy、Istio、Nginx)或框架层(gRPC-Go、go-kit)已提供更健壮的方案;真要手写,也只应在 HTTP 客户端侧做轻量轮询,且必须处理健康剔除、原子计数、连接复用三件事。
gRPC-Go 启用 round_robin 必须显式配置 service config
gRPC-Go v1.27+ 默认禁用客户端负载均衡,grpc.WithBalancerName("round_robin") 已被弃用,直接调用会 panic。正确做法是:
- 导入 resolver:确保已
_ "google.golang.org/grpc/resolver/dns"或_ "google.golang.org/grpc/resolver/passthrough" - 目标 URL 用
dns:///svc.example.com或passthrough:///10.0.1.1:8080,10.0.1.2:8080 - 传入默认 service config:
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`) - 若用
passthrough且未配 resolver,必须手动指定 balancer,否则报错:"no load balancing policy configured"
HTTP 客户端轮询不能靠 new http.Client 拼地址
http.Client 本身不维护后端列表,也不感知健康状态。常见错误是每次请求都拼新 URL 或 new 新 client。正确路径是自定义 RoundTripper:
- 用
atomic.Uint64维护索引,避免锁竞争;取模前先检查len(backends) > 0 -
Transport必须复用,并设MaxIdleConnsPerHost = 100,否则连接池失效 - 遇到
connection refused或 HTTP 5xx,应临时标记 endpoint 退避,存入sync.Map,键为host:port,值为time.Now().Add(30 * time.Second) - 别忽略
context.DeadlineExceeded:它会中断 dial,但不会自动触发重试或切换节点
加权轮询需服务注册中心配合下发权重
纯本地加权轮询(比如按 CPU 核心数硬编码权重)在动态扩缩容时完全失效。真实场景中,权重必须由注册中心统一管理:
- Consul/Etcd 中每个服务实例注册时带
weight字段(如{"weight": 3}) - 客户端拉取实例列表后,解析
Instance.Weight,而非自己猜 - 平滑加权轮询推荐用“最大最小连接差”法,而非简单累加权重取模——后者在权重差异大时易导致抖动
- 权重变更必须触发
instances缓存 reload,且重置计数器,否则可能越界 panic
最易被忽略的是 DNS 缓存:用域名当后端地址时,http.Transport 默认缓存解析结果长达 30 秒,扩容后新实例根本进不来流量;要么直连 IP,要么重写 DialContext 配合自定义 DNS 解析器。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











