平衡器的核心目的就是用于向服务端发起链接,主要包括
- 子链接的维护,每个 target 可能对应多个地址(类似一个服务有多个副本),进行子链接的新增与删除
- 当平衡器状态发生变化,更新
ClientConn
的状态,自定义选择器,从而让客户端选择具体子链接进行发送 - 在满足一定条件的情况下,执行解析器操作。如解析器由阻塞状态 --> 非阻塞状态
整体流程
实现原理
代码路径:/balancer/balancer.go
,在这个文件中一共定义了五个接口
- Builder 接口,主要用来创建平衡器
- SubConn 接口,主要用来负责具体的链接(这里的链接指的是一对实际建立链接的客户端与服务器)
- Picker 接口,主要是从众多
SubConn
中按照某个策略选择一个链接进行数据传输,也就是 选择器 - Balancer 接口,主要是更新
ClientConn
的状态,更新SubConn
状态 - ClientConn 接口,主要是负责链路的维护,包括创建、移除、更新一个子链路,更新
ClientConn
状态
所以平衡器也跟解析器一样,由两部分组成平衡器构建器与平衡器,内部在封装到一个结构体实现平衡器功能
平衡器构建器注册
平衡器构建器通过注册的方式将自己平衡器的构建方法存入全局变量中,当需要使用的时候则直接根据名称获取
-
使用一个全局的 map
var m = make(map[string]Builder)
进行平衡器构建器Builder
的存储。k/v 分别对应的是构建 构建器的名称以及构建器的实现方法。 -
平衡器构建器可以通过
Register
函数存储到这个 map 中1
2
3
4
5
6
7
8
9
10
11
12//Register 注册平衡器构建器
func Register(b Builder) {
m[strings.ToLower(b.Name())] = b
}
//Get 获取平衡器构建器,如果找不到则返回nil
func Get(name string) Builder {
if b, ok := m[strings.ToLower(name)]; ok {
return b
}
return nil
} -
有了存储与添加,它还增加了一个删除操作
1
2
3
4
5
6
7
8//仅做测试使用,去掉某一个平衡器解析器
func unregisterForTesting(name string) {
delete(m, name)
}
func init() {
internal.BalancerUnregister = unregisterForTesting
}删除操作则是利用
init
给BalancerUnregister
复制一个 删除函数unregisterForTesting
-
而一个普通的平衡器构建器(已轮询平衡器构建器为例
rrPickerBuilder
),则是利用 init 函数直接初始化到这个全局map中,那么就可以在需要使用的时候,直接从全局map 中获取1
2
3
4
5
6
7
8
9
10
11const Name = "round_robin"
//初始化一个平衡器构建器对洗那个
func newBuilder() balancer.Builder {
return base.NewBalancerBuilder(Name, &rrPickerBuilder{}, base.Config{HealthCheck: true})
}
//初始化将 round_robin平衡器构建器存入全局 map 中
func init() {
balancer.Register(newBuilder())
}
构建平衡器
有了平衡器构建器之后,通过选定的平衡器构建器进行平衡器的构建
-
设置通过哪个平衡器构建器获取平衡器
1
2
3
4
5
6
7
8
9
10
11
12var serviceConfig = `{
"loadBalancingConfig": [
{
"grpclb": {
"childPolicy": [
{"round_robin": ""},
]
}
}
]
}`
grpc.WithDefaultServiceConfig(serviceConfig)如果不设置负载均衡构建器名称,那么grpc-go 会默认使用
round_robin
构建平衡器 -
指定了构建器之后就会在构建链接过程中调用构建函数
1
2
3
4
5
6
7func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {
//...
cc.balancerWrapper = newCCBalancerWrapper(cc, balancer.BuildOptions{
//...
})
//...
}在建立连接的过程中,通过