2022-12-19 17:39:31 +00:00
|
|
|
package balancer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"google.golang.org/grpc/balancer"
|
|
|
|
"google.golang.org/grpc/connectivity"
|
|
|
|
"google.golang.org/grpc/grpclog"
|
|
|
|
"google.golang.org/grpc/resolver"
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
balancer.Register(newCustomPickfirstBuilder())
|
|
|
|
}
|
|
|
|
|
|
|
|
// logger is referenced in pickfirst.go.
|
|
|
|
// The gRPC library uses the same component name.
|
|
|
|
var logger = grpclog.Component("balancer")
|
|
|
|
|
|
|
|
func newCustomPickfirstBuilder() balancer.Builder {
|
|
|
|
return &customPickfirstBuilder{}
|
|
|
|
}
|
|
|
|
|
|
|
|
type customPickfirstBuilder struct{}
|
|
|
|
|
|
|
|
func (*customPickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer {
|
|
|
|
return &customPickfirstBalancer{
|
|
|
|
pickfirstBalancer: pickfirstBalancer{cc: cc},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*customPickfirstBuilder) Name() string {
|
|
|
|
return "pick_first_custom"
|
|
|
|
}
|
|
|
|
|
|
|
|
// customPickfirstBalancer overrides UpdateClientConnState of pickfirstBalancer.
|
|
|
|
type customPickfirstBalancer struct {
|
|
|
|
pickfirstBalancer
|
|
|
|
|
|
|
|
activeAddr resolver.Address
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *customPickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error {
|
|
|
|
for _, a := range state.ResolverState.Addresses {
|
|
|
|
// This hack preserves an existing behavior in our client-side
|
|
|
|
// load balancing where if the first address in a shuffled list
|
|
|
|
// of addresses matched the currently connected address, it would
|
|
|
|
// be an effective no-op.
|
|
|
|
if a.Equal(b.activeAddr) {
|
2022-12-19 18:53:05 +00:00
|
|
|
break
|
2022-12-19 17:39:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to make a new SubConn with a single address so we can
|
|
|
|
// track a successful connection explicitly. If we were to pass
|
|
|
|
// a list of addresses, we cannot assume the first address was
|
|
|
|
// successful and there is no way to extract the connected address.
|
|
|
|
sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{})
|
|
|
|
if err != nil {
|
|
|
|
logger.Warningf("balancer.customPickfirstBalancer: failed to create new SubConn: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.subConn != nil {
|
|
|
|
b.cc.RemoveSubConn(b.subConn)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy-pasted from pickfirstBalancer.UpdateClientConnState.
|
|
|
|
{
|
|
|
|
b.subConn = sc
|
|
|
|
b.state = connectivity.Idle
|
|
|
|
b.cc.UpdateState(balancer.State{
|
|
|
|
ConnectivityState: connectivity.Idle,
|
|
|
|
Picker: &picker{result: balancer.PickResult{SubConn: b.subConn}},
|
|
|
|
})
|
|
|
|
b.subConn.Connect()
|
|
|
|
}
|
|
|
|
|
|
|
|
b.activeAddr = a
|
|
|
|
|
|
|
|
// We now have a new subConn with one address.
|
|
|
|
// Break the loop and call UpdateClientConnState
|
|
|
|
// with the full set of addresses.
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// This will load the full set of addresses but leave the
|
|
|
|
// newly created subConn alone.
|
|
|
|
return b.pickfirstBalancer.UpdateClientConnState(state)
|
|
|
|
}
|