go中http权重路由需自定义handler实现,因servemux不支持;grpc推荐用balancer+picker,利用address.attributes存权重并用原子计数器精确分发。

Go 里用 http.Handler 做权重路由,别碰 net/http.ServeMux
标准 ServeMux 不支持权重,硬塞逻辑会破坏路由匹配顺序,导致 404 或错发请求。真实灰度场景里,你得自己接管分发逻辑。
推荐用一个轻量 Handler 包裹下游服务,按权重查表后透传:http.RoundTrip 或 httputil.NewSingleHostReverseProxy 都行,但注意 Director 要重写 Request.URL 和 Host 头。
- 权重配置建议放内存 map +
sync.RWMutex,避免每次请求都锁全表 - 别用 float64 做累计比较(如 rand.Float64()
- 如果后端是 gRPC,别强行套 HTTP Handler,直接用
grpc.RoundRobin+ 自定义Resolver更稳
gorilla/mux 路由器加权重?它根本不提供这个接口
gorilla/mux 是路径/方法匹配器,不是负载均衡器。它的 Subrouter 和 MatcherFunc 只能做条件判断,没法原生支持“把 20% 请求发给 v2”。强行在 MatcherFunc 里塞随机逻辑,会导致健康检查、中间件执行顺序混乱,且无法动态 reload 权重。
- 真要用
gorilla/mux,只把它当入口网关,路由到不同http.Handler实例,权重逻辑下沉到 handler 内部 - 别在
MatcherFunc里调用外部配置中心或 DB,延迟会卡死整个 mux - 它的
Vars()和URLQuery()是线程安全的,但自定义字段(比如权重值)必须自己加锁
权重更新不生效?大概率是没处理好配置热加载和连接复用
HTTP 连接池(http.Transport)默认复用底层 TCP 连接,如果新权重指向了新后端地址,但旧连接还挂在老 IP 上,就会持续打过去——看起来像“权重没变”。
立即学习“go语言免费学习笔记(深入)”;
- 每次更新权重时,要清空对应后端的连接池:
transport.IdleConnTimeout = 0然后重建,或调用transport.CloseIdleConnections() - 配置监听建议用
fsnotify监控文件,或用etcd的 watch 接口;别轮询,轮询间隔稍大就错过灰度窗口 - 权重变更后,记录
log.Printf("weight updated: %v -> %v", old, new),别只靠 metrics 推断
gRPC 场景下,balancer 接口比 HTTP 手动分发更可靠
HTTP 权重路由本质是客户端侧决策,而 gRPC 的 balancer.Balancer 是 SDK 内置机制,天然支持连接管理、健康探测、权重更新回调。用 round_robin + 自定义 Picker 就能实现精确权重。
- 实现
Pick方法时,用原子计数器替代随机数,避免小流量下统计偏差(比如 10qps、权重 95:5,rand 可能连续 10 次都选 A) - 别在
Pick里做网络 IO,它被高频调用,阻塞会导致整个 client 卡住 - gRPC 的
resolver返回的Address支持带Attributes字段,权重可以直接塞进去,Picker 拿来用,不用额外查表











