目录
一、为啥我们要路由策略:
二、基于gRPC 路由策略
一、为啥我们要路由策略:
我们可以重新回到调用方发起 RPC 调用的流程。在 RPC 发起真实请求的时候,有一个步骤就是从服务提供方节点集合里面选择一个合适的节点(就是我们常说的负载均衡),那我们是不是可以在选择节点前加上“筛选逻辑”,把符合我们要求的节点筛选出来。这就是路由
举个例子:比如我们要求新上线的节点只允许某个IP可以调用,那我们的注册中心会把这条规则下发到服务调用方。在调用方收到规则后,在选择具体节点前,会先通过筛选规则过滤节点集合,按照这个例子的逻辑,最后会过滤出一个节点。整个整个RPC 调用过程如下。
从上图可以看出路由就是从服务发现所有节点,帅选出合适节点的过程。
二、基于gRPC 路由策略
下面代码是加权轮询负载均衡算法,我们加上了Filter 对节点进行筛选,那么这个筛选的规则就是路由策略,加权轮询负载均衡算法详细解释可以参考负载均衡算法
type Filter func(info balancer.PickInfo, Group string) bool
type balanceWeightBuild struct {
Filter Filter
}
func (b balanceWeightBuild) Build(info base.PickerBuildInfo) balancer.Picker {
result := make([]*SubConnInfo, 0, len(info.ReadySCs))
for k, v := range info.ReadySCs {
weight := v.Address.Attributes.Value("weight").(string)
group := v.Address.Attributes.Value("group").(string)
w, _ := strconv.Atoi(weight)
result = append(result, &SubConnInfo{
Conn: k,
Weight: w,
CurrentWeight: w,
EffectiveWeight: w,
Group: group,
})
}
return &balancepick{
Address: result,
Filter: b.Filter,
}
}
type balancepick struct {
Address []*SubConnInfo
Filter Filter
}
type SubConnInfo struct {
Conn balancer.SubConn
Weight int
Group string
CurrentWeight int
EffectiveWeight int
lock sync.Mutex
}
func (b *balancepick) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
if len(b.Address) == 0 {
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
}
if b.Filter == nil {
b.Filter = func(info balancer.PickInfo, Group string) bool {
return true
}
}
res := make([]*SubConnInfo, 0, 5)
for _, re := range b.Address {
if !b.Filter(info, re.Group) {
continue
}
res = append(res, re)
}
if len(res) == 0 {
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
}
totalWeitht := 0
var current *SubConnInfo
for _, v := range res {
v.lock.Lock()
totalWeitht += v.EffectiveWeight
v.CurrentWeight += v.EffectiveWeight
if current == nil || current.CurrentWeight < v.CurrentWeight {
current = v
}
v.lock.Unlock()
}
current.lock.Lock()
current.CurrentWeight -= totalWeitht
current.lock.Unlock()
return balancer.PickResult{
SubConn: current.Conn,
Done: func(info balancer.DoneInfo) {
current.lock.Lock()
if info.Err == nil && current.EffectiveWeight == math.MaxInt {
current.EffectiveWeight--
return
}
if info.Err != nil && current.EffectiveWeight == 0 {
current.EffectiveWeight++
return
}
if info.Err != nil {
current.EffectiveWeight--
} else {
current.EffectiveWeight++
}
current.lock.Unlock()
},
}, nil
}