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
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 |
|
}
|
|
|