grpc go中grpc.roundrobin未生效是因为客户端默认不启用负载均衡,需显式配置支持lb的resolver(如dns:///)并设置服务配置{"loadbalancingpolicy": "round_robin"},且resolver必须返回多个地址。

Go 里用 grpc.RoundRobin 为什么没生效?
因为 gRPC Go 客户端默认不启用负载均衡,grpc.RoundRobin 只是内置的 ResolverBuilder 名称,不是开关。它只在你显式配置了支持 LB 的 resolver(比如 dns 或自定义)且服务端地址是域名或带多个 IP 的字符串时才起作用。
- 直接传
"127.0.0.1:8001,127.0.0.1:8002"这种逗号分隔字符串?RoundRobin不识别——gRPC 认为这是单个 endpoint,不会拆分 - 要用
dns:///myservice并配合本地/etc/hosts或真实 DNS 返回多条 A 记录,或者自己实现resolver.Builder - 客户端必须显式启用:创建
grpc.Dial时传grpc.WithTransportCredentials(insecure.NewCredentials())(开发用),再加grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`) - 注意:gRPC v1.59+ 把
round_robin设为默认策略,但前提是 resolver 确实返回了多个地址;否则 fallback 到 pick_first
如何让 Go 客户端做带权重的随机选择?
标准库和 gRPC 默认都不支持权重——round_robin 和 pick_first 都是等权的。要加权重,得自己写 balancer.Builder,核心是在 Picker.Pick() 里按权重采样。
- 权重不能存在服务发现元数据外的地方:建议从 DNS SRV 记录(
priority/weight字段)或 Consul/Etcd 的 KV 中读取,避免硬编码 - 别在每次
Pick()时重新计算累积权重数组——用sync.Map缓存已解析的 endpoint → weight 映射,并监听服务变更事件刷新 - 简单验证可用性:启动两个后端实例,分别注册为
svc-1(10)和svc-2(90),发 1000 次请求,检查svc-2接收量是否接近 900 - 示例片段:
func (p *weightedPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) { r := rand.Intn(p.totalWeight) for _, ep := range p.endpoints { if r < ep.weight { return balancer.PickResult{SubConn: ep.sc}, nil } r -= ep.weight } return balancer.PickResult{SubConn: p.endpoints[0].sc}, nil }
服务端用 gin 或 echo 做反向代理轮询,靠谱吗?
不推荐。HTTP 反向代理层做轮询会破坏 gRPC over HTTP/2 的语义,导致流式调用失败、Header 丢失、超时传递异常,而且无法感知后端健康状态。
-
gin的proxy中间件本质是 HTTP/1.1 转发,而 gRPC 必须走 HTTP/2 —— 即使你用h2c,也绕不开连接复用和 stream 生命周期管理 - 如果非要用,至少确保:后端开启
h2c(无 TLS)、客户端禁用 TLS(grpc.WithTransportCredentials(insecure.NewCredentials()))、代理设置Upgrade和Connectionheader - 真正可控的方式是用
envoy或linkerd:它们原生支持 gRPC,能解析:authority、透传 metadata、做健康检查、支持权重路由 - 小规模内部服务可退一步:客户端直连 + DNS 轮询 + 客户端重试,比在 HTTP 层硬塞更轻量、更可靠
Consul 注册时填的 Weights,Go 客户端能自动读到吗?
不能。Consul 的 Weights(即 Passing/Critical 状态权重)是用于服务健康筛选的,不是 LB 权重。gRPC 的 resolver 不解析 Consul 健康 API 返回的 Weights 字段。
立即学习“go语言免费学习笔记(深入)”;
- 想用 Consul 做权重 LB,得把权重写进 service 的
Meta字段(如{"lb_weight": "75"}),然后在自定义 resolver 的ResolveNow()里调用HealthAPI.Service()拉取 meta 并构造带权 endpoint 列表 - 注意 meta 是字符串 map,需要自己 parse 数字并做类型校验,避免因 typo 导致权重全为 0
- Consul 的
Blocking Query要配好wait参数(比如5m),否则频繁轮询会压垮 agent - 别依赖 Consul UI 里看到的 “Weights” 数值——那是健康检查权重,和流量分发无关
真正的难点不在代码怎么写,而在服务发现数据的一致性:resolver 拉到的地址列表、权重、健康状态,这三个东西必须原子更新。任何一环错位,都会导致请求打到已下线或过载的实例上。










