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.
98 lines
2.3 KiB
98 lines
2.3 KiB
package balancer |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"google.golang.org/grpc/balancer" |
|
"google.golang.org/grpc/balancer/base" |
|
"google.golang.org/grpc/grpclog" |
|
"google.golang.org/grpc/resolver" |
|
"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 |
|
if addr.Attributes == nil { |
|
return |
|
} |
|
|
|
val := addr.Attributes.Value(WeightKey) |
|
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 |
|
}
|
|
|