You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

142 lines
3.3 KiB

package balancer
import (
"encoding/json"
"errors"
"fmt"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/base"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/resolver"
"sonet/pkg/protocol/deliver"
"sonet/pkg/utils/logger"
"strconv"
)
const ConsistentHash = "consistent_hash_x"
var ConsistentHashKey = "consistent-hash"
func InitConsistentHashBuilder() {
balancer.Register(newConsistentHashBuilder())
}
// newConsistentHashBuilder creates a new ConsistentHash balancer builder.
func newConsistentHashBuilder() balancer.Builder {
return base.NewBalancerBuilder(
ConsistentHash,
&consistentHashPickerBuilder{},
base.Config{HealthCheck: true},
)
}
type consistentHashPickerBuilder struct{}
func (b *consistentHashPickerBuilder) Build(buildInfo base.PickerBuildInfo) balancer.Picker {
// logger.Infof("consistentHashPicker: newPicker called with buildInfo: %v", buildInfo)
if len(buildInfo.ReadySCs) == 0 {
return base.NewErrPicker(balancer.ErrNoSubConnAvailable)
}
subConns := make(map[string]balancer.SubConn)
var nodes []*deliver.PickNode
for sc, conInfo := range buildInfo.ReadySCs {
weight := GetWeight(conInfo.Address)
node := &deliver.PickNode{
Key: conInfo.Address.Addr,
Weight: weight,
}
nodes = append(nodes, node)
subConns[node.Key] = sc
}
picker := deliver.NewConsistentHashPicker(nodes, deliver.DefaultReplicas, deliver.DefaultSalt)
picker.Init()
return &postalConsistentHashPicker{
subConns: subConns,
picker: picker,
}
}
type postalConsistentHashPicker struct {
subConns map[string]balancer.SubConn
picker *deliver.ConsistentHashPicker
}
func (p *postalConsistentHashPicker) Pick(info balancer.PickInfo) (ret balancer.PickResult, err error) {
key, ok := info.Ctx.Value(ConsistentHashKey).(string)
if !ok || key == "" {
panic(errors.New("empty consistent hash key"))
}
node, ok := p.picker.Pick(key)
if ok {
ret.SubConn = p.subConns[node.Key]
}
return
}
// consistentHashPicker
// Deprecated
type consistentHashPicker struct {
subConns map[string]balancer.SubConn
hash *Ketama
}
func (p *consistentHashPicker) Pick(info balancer.PickInfo) (ret balancer.PickResult, err error) {
key, ok := info.Ctx.Value(ConsistentHashKey).(string)
if !ok || key == "" {
//key = strconv.Itoa(rand.Intn(65536))
//grpclog.Warning("empty consistent hash key")
panic(errors.New("empty consistent hash key"))
}
targetAddr, ok := p.hash.Get(key)
if ok {
ret.SubConn = p.subConns[targetAddr]
}
return
}
func wrapAddr(addr string, idx int) string {
return fmt.Sprintf("%s-%d", addr, idx)
}
func GetWeight(addr resolver.Address) (weight int) {
weight = DefaultWeight
var val any = nil
// from metadata...
if marshal, ok := addr.Metadata.(string); ok {
m := make(map[string]any)
err := json.Unmarshal([]byte(marshal), &m)
if err != nil {
logger.Error("unmarshal metadata error: ", err)
} else {
val = m[WeightKey]
}
}
// from attributes...
if addr.Attributes != nil {
val = addr.Attributes.Value(WeightKey)
}
if val == nil {
return
}
switch val.(type) {
case int:
weight = val.(int)
case string:
w, err := strconv.Atoi(val.(string))
if err != nil {
grpclog.Errorf("instance weight format error: %v\n", val)
return
}
weight = w
//default:
// grpclog.Errorf("instance weight value type not string: %v\n", val)
}
return
}