gRPC Go客户端默认使用pick_first策略,仅连接首个地址,其余被忽略;必须显式启用resolver与balancer协同机制,如passthrough:///或dns:///配合round_robin配置才能实现轮询。

Go 本身不提供开箱即用的负载均衡器,客户端侧必须自己组合 http.RoundTripper 或 grpc.Balancer,服务端侧得靠外部组件(如 Envoy、Nginx)或 Service Mesh;硬编码地址、直接复用 http.DefaultClient 或只传多个 endpoint 给 grpc.Dial,全都不生效。
为什么 grpc.Dial("10.0.1.10:8080,10.0.1.11:8080") 不会轮询
gRPC Go 客户端默认行为是 pick_first:只连第一个地址,其余被静默忽略。即使你传入逗号分隔的字符串,它也不会自动解析或分发——这是常见误解根源。
- 必须显式启用名字解析(
resolver)+ 负载均衡器(balancer)协同机制,缺一不可 - 想用内置轮询,得配
grpc.WithBalancerName("round_robin"),同时确保 resolver 返回的是多个有效address.Address(不能含空字符串或非法格式) - 推荐绕过自研 resolver:用
passthrough:///10.0.1.10:8080,10.0.1.11:8080协议,再配合WithBalancerName,简单可靠 - 错误现象包括:
rpc error: code = Unavailable desc = connection closed、监控里流量始终打在同一台、日志里没看到round_robin switch
怎么用 http.RoundTripper 实现轮询(Round Robin)
这不是改 http.Client,而是实现一个带状态的 RoundTripper,每次请求前动态选后端。核心是复用 http.DefaultTransport 的连接池和 TLS 复用能力,而不是自己 new 一个 http.Transport。
- 不能在
RoundTrip里直接用rand.Intn——没有种子控制会导致测试不可重现,且并发下原子性难保;必须用atomic.AddUint64维护索引 - 后端列表建议用
[]string存IP:Port,避免 DNS 缓存问题;若非要用域名,得重写Transport.DialContext+ 自定义 DNS 解析逻辑 - 每次转发前必须克隆原始
*http.Request,否则修改req.URL.Host会影响后续中间件或日志 - 别忘了设置
req.Header.Set("Host", backendHost),否则后端可能因 Host 不匹配返回 404
健康检查和故障剔除为什么不能省
真实场景下,后端挂了不能还往死里发请求。Go 没有内置健康检查机制,得自己加定时探测 + 状态缓存。容易踩的坑是用 time.Tick 做探测——它不防重入,多个 goroutine 可能并发调用 http.Get,压垮监控接口;还有人把健康状态存在 map 里但没加锁,导致读写冲突 panic。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用
sync.Map存后端健康状态(map[string]bool),避免锁竞争 - 探测用
http.Client并显式设Timeout: 2 * time.Second,防止卡住整个探测周期 - 剔除故障节点后,轮询索引要重置,否则可能长期跳过有效节点
- 恢复逻辑别用“连续成功 3 次”,而用“最后一次成功时间 > 30s 前”,更稳
http/2 和 TLS 配置对负载均衡的影响
如果后端是 HTTPS,http.Transport 默认会启用 HTTP/2,但某些老版本 Nginx 或自签名服务不兼容,会导致 http2: server sent GOAWAY and closed the connection 错误,请求直接失败。
- 可通过设置
Transport.ForceAttemptHTTP2 = false临时禁用 HTTP/2 - 若需保留 HTTP/2,得确保后端支持 ALPN,并校验证书链完整性和 SNI 配置
- 自签名证书场景下,必须显式配置
Transport.TLSClientConfig.InsecureSkipVerify = true或注入自定义 CA - 注意:禁用 HTTP/2 后,连接复用仍有效,但单连接并发能力下降,高吞吐场景需权衡
轮询索引的原子性、请求克隆的必要性、健康状态的异步更新时机——这三个点最容易在压测或扩容时突然暴露,不是写完就能跑通,得真正在多实例、网络抖动、证书轮换等条件下验证过才算落地。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










