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.
 
 

117 lines
2.7 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/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 {
// grpclog.Infof("consistentHashPicker: newPicker called with buildInfo: %v", buildInfo)
if len(buildInfo.ReadySCs) == 0 {
return base.NewErrPicker(balancer.ErrNoSubConnAvailable)
}
picker := &consistentHashPicker{
subConns: make(map[string]balancer.SubConn),
hash: NewKetama(DefaultReplicas, nil),
}
for sc, conInfo := range buildInfo.ReadySCs {
weight := GetWeight(conInfo.Address)
for i := 0; i < weight; i++ {
node := wrapAddr(conInfo.Address.Addr, i)
picker.hash.Add(node)
picker.subConns[node] = sc
}
}
return picker
}
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
}