跳到主要内容

负载均衡

问题

Go 微服务中的负载均衡策略有哪些?gRPC 的客户端负载均衡是怎么工作的?

答案

负载均衡类型

Go 微服务主流选择

Go 微服务通常使用客户端负载均衡:调用方从注册中心获取实例列表,自己选择目标实例。gRPC 原生支持这种模式。

gRPC 客户端负载均衡

// 使用 round_robin 策略
conn, _ := grpc.Dial(
"etcd:///user-service",
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`),
)

常见策略

策略原理适用场景
Round Robin轮流分配实例性能相近
Weighted Round Robin按权重分配实例性能不同
Random随机选择简单场景
Least Connections选连接最少的长连接服务
一致性哈希相同 key 固定实例有状态/缓存场景

自定义负载均衡器

import (
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/base"
)

// 加权轮询 Picker
type weightedPicker struct {
subConns []balancer.SubConn
weights []int
current int
}

func (p *weightedPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) {
// 加权轮询选择
sc := p.selectByWeight()
return balancer.PickResult{SubConn: sc}, nil
}

// 注册自定义策略
func init() {
balancer.Register(
base.NewBalancerBuilder("weighted_round_robin", &weightedPickerBuilder{}, base.Config{}),
)
}

// 使用
conn, _ := grpc.Dial(target,
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"weighted_round_robin"}`),
)

一致性哈希

适用于需要将相同用户/key 路由到固定实例的场景(如缓存):

type consistentHashPicker struct {
ring *hashring.HashRing // 第三方一致性哈希库
}

func (p *consistentHashPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
// 从 Context 中取 hash key
key, _ := info.Ctx.Value(hashKeyCtx{}).(string)
node := p.ring.GetNode(key)
return balancer.PickResult{SubConn: node.SubConn}, nil
}

常见面试问题

Q1: 客户端负载均衡 vs 服务端负载均衡?

答案

维度客户端 LB服务端 LB
调用链路Client → ServerClient → LB → Server
性能无额外跳转多一跳
复杂度客户端需集成 SDK客户端无感知
代表gRPC、DubboNginx、Kong
Go 推荐✅ 内部 gRPC 调用外部 HTTP 入口

Q2: gRPC 的 round_robin 有什么局限?

答案

  • 不感知实例负载,高负载和低负载实例均分流量
  • 不支持权重,无法区分不同规格的实例
  • 不处理慢实例,不会自动避开响应慢的节点

生产环境通常需要 P2C (Power of 2 Choices) 策略:随机选 2 个实例,选负载更低的那个。go-zero 和 Kratos 都内置了 P2C。

相关链接