grpclb should connect to the second balancer (#1181)
grpclb needs to connect the second resolved balancer address when the first balancer disconnects. If grpclb gets 2 resolved addresses: balancer1 and balancer2. When balancer1 disconnects, grpclb should automatically start to use balancer2.
This commit is contained in:
116
grpclb/grpclb.go
116
grpclb/grpclb.go
@ -111,7 +111,7 @@ type balancer struct {
|
|||||||
rand *rand.Rand
|
rand *rand.Rand
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *balancer) watchAddrUpdates(w naming.Watcher, ch chan remoteBalancerInfo) error {
|
func (b *balancer) watchAddrUpdates(w naming.Watcher, ch chan []remoteBalancerInfo) error {
|
||||||
updates, err := w.Next()
|
updates, err := w.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -121,10 +121,6 @@ func (b *balancer) watchAddrUpdates(w naming.Watcher, ch chan remoteBalancerInfo
|
|||||||
if b.done {
|
if b.done {
|
||||||
return grpc.ErrClientConnClosing
|
return grpc.ErrClientConnClosing
|
||||||
}
|
}
|
||||||
var bAddr remoteBalancerInfo
|
|
||||||
if len(b.rbs) > 0 {
|
|
||||||
bAddr = b.rbs[0]
|
|
||||||
}
|
|
||||||
for _, update := range updates {
|
for _, update := range updates {
|
||||||
switch update.Op {
|
switch update.Op {
|
||||||
case naming.Add:
|
case naming.Add:
|
||||||
@ -173,21 +169,11 @@ func (b *balancer) watchAddrUpdates(w naming.Watcher, ch chan remoteBalancerInfo
|
|||||||
}
|
}
|
||||||
// TODO: Fall back to the basic round-robin load balancing if the resulting address is
|
// TODO: Fall back to the basic round-robin load balancing if the resulting address is
|
||||||
// not a load balancer.
|
// not a load balancer.
|
||||||
if len(b.rbs) > 0 {
|
|
||||||
// For simplicity, always use the first one now. May revisit this decision later.
|
|
||||||
if b.rbs[0] != bAddr {
|
|
||||||
select {
|
select {
|
||||||
case <-ch:
|
case <-ch:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
// Pick a random one from the list, instead of always using the first one.
|
ch <- b.rbs
|
||||||
if l := len(b.rbs); l > 1 {
|
|
||||||
tmpIdx := b.rand.Intn(l - 1)
|
|
||||||
b.rbs[0], b.rbs[tmpIdx] = b.rbs[tmpIdx], b.rbs[0]
|
|
||||||
}
|
|
||||||
ch <- b.rbs[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +247,7 @@ func (b *balancer) processServerList(l *lbpb.ServerList, seq int) {
|
|||||||
func (b *balancer) callRemoteBalancer(lbc lbpb.LoadBalancerClient, seq int) (retry bool) {
|
func (b *balancer) callRemoteBalancer(lbc lbpb.LoadBalancerClient, seq int) (retry bool) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
stream, err := lbc.BalanceLoad(ctx, grpc.FailFast(false))
|
stream, err := lbc.BalanceLoad(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Printf("Failed to perform RPC to the remote balancer %v", err)
|
grpclog.Printf("Failed to perform RPC to the remote balancer %v", err)
|
||||||
return
|
return
|
||||||
@ -340,32 +326,98 @@ func (b *balancer) Start(target string, config grpc.BalancerConfig) error {
|
|||||||
}
|
}
|
||||||
b.w = w
|
b.w = w
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
balancerAddrCh := make(chan remoteBalancerInfo, 1)
|
balancerAddrsCh := make(chan []remoteBalancerInfo, 1)
|
||||||
// Spawn a goroutine to monitor the name resolution of remote load balancer.
|
// Spawn a goroutine to monitor the name resolution of remote load balancer.
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
if err := b.watchAddrUpdates(w, balancerAddrCh); err != nil {
|
if err := b.watchAddrUpdates(w, balancerAddrsCh); err != nil {
|
||||||
grpclog.Printf("grpc: the naming watcher stops working due to %v.\n", err)
|
grpclog.Printf("grpc: the naming watcher stops working due to %v.\n", err)
|
||||||
close(balancerAddrCh)
|
close(balancerAddrsCh)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
// Spawn a goroutine to talk to the remote load balancer.
|
// Spawn a goroutine to talk to the remote load balancer.
|
||||||
go func() {
|
go func() {
|
||||||
var cc *grpc.ClientConn
|
var (
|
||||||
for {
|
cc *grpc.ClientConn
|
||||||
rb, ok := <-balancerAddrCh
|
// ccError is closed when there is an error in the current cc.
|
||||||
|
// A new rb should be picked from rbs and connected.
|
||||||
|
ccError chan struct{}
|
||||||
|
rb *remoteBalancerInfo
|
||||||
|
rbs []remoteBalancerInfo
|
||||||
|
rbIdx int
|
||||||
|
)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if ccError != nil {
|
||||||
|
select {
|
||||||
|
case <-ccError:
|
||||||
|
default:
|
||||||
|
close(ccError)
|
||||||
|
}
|
||||||
|
}
|
||||||
if cc != nil {
|
if cc != nil {
|
||||||
cc.Close()
|
cc.Close()
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
var ok bool
|
||||||
|
select {
|
||||||
|
case rbs, ok = <-balancerAddrsCh:
|
||||||
if !ok {
|
if !ok {
|
||||||
// b is closing.
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
foundIdx := -1
|
||||||
|
if rb != nil {
|
||||||
|
for i, trb := range rbs {
|
||||||
|
if trb == *rb {
|
||||||
|
foundIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundIdx >= 0 {
|
||||||
|
if foundIdx >= 1 {
|
||||||
|
// Move the address in use to the beginning of the list.
|
||||||
|
b.rbs[0], b.rbs[foundIdx] = b.rbs[foundIdx], b.rbs[0]
|
||||||
|
rbIdx = 0
|
||||||
|
}
|
||||||
|
continue // If found, don't dial new cc.
|
||||||
|
} else if len(rbs) > 0 {
|
||||||
|
// Pick a random one from the list, instead of always using the first one.
|
||||||
|
if l := len(rbs); l > 1 && rb != nil {
|
||||||
|
tmpIdx := b.rand.Intn(l - 1)
|
||||||
|
b.rbs[0], b.rbs[tmpIdx] = b.rbs[tmpIdx], b.rbs[0]
|
||||||
|
}
|
||||||
|
rbIdx = 0
|
||||||
|
rb = &rbs[0]
|
||||||
|
} else {
|
||||||
|
// foundIdx < 0 && len(rbs) <= 0.
|
||||||
|
rb = nil
|
||||||
|
}
|
||||||
|
case <-ccError:
|
||||||
|
ccError = nil
|
||||||
|
if rbIdx < len(rbs)-1 {
|
||||||
|
rbIdx++
|
||||||
|
rb = &rbs[rbIdx]
|
||||||
|
} else {
|
||||||
|
rb = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rb == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if cc != nil {
|
||||||
|
cc.Close()
|
||||||
|
}
|
||||||
// Talk to the remote load balancer to get the server list.
|
// Talk to the remote load balancer to get the server list.
|
||||||
var err error
|
var err error
|
||||||
creds := config.DialCreds
|
creds := config.DialCreds
|
||||||
|
ccError = make(chan struct{})
|
||||||
if creds == nil {
|
if creds == nil {
|
||||||
cc, err = grpc.Dial(rb.addr, grpc.WithInsecure())
|
cc, err = grpc.Dial(rb.addr, grpc.WithInsecure())
|
||||||
} else {
|
} else {
|
||||||
@ -379,22 +431,24 @@ func (b *balancer) Start(target string, config grpc.BalancerConfig) error {
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Printf("Failed to setup a connection to the remote balancer %v: %v", rb.addr, err)
|
grpclog.Printf("Failed to setup a connection to the remote balancer %v: %v", rb.addr, err)
|
||||||
return
|
close(ccError)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
b.seq++ // tick when getting a new balancer address
|
b.seq++ // tick when getting a new balancer address
|
||||||
seq := b.seq
|
seq := b.seq
|
||||||
b.next = 0
|
b.next = 0
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
go func(cc *grpc.ClientConn) {
|
go func(cc *grpc.ClientConn, ccError chan struct{}) {
|
||||||
lbc := lbpb.NewLoadBalancerClient(cc)
|
lbc := lbpb.NewLoadBalancerClient(cc)
|
||||||
for {
|
b.callRemoteBalancer(lbc, seq)
|
||||||
if retry := b.callRemoteBalancer(lbc, seq); !retry {
|
|
||||||
cc.Close()
|
cc.Close()
|
||||||
return
|
select {
|
||||||
|
case <-ccError:
|
||||||
|
default:
|
||||||
|
close(ccError)
|
||||||
}
|
}
|
||||||
}
|
}(cc, ccError)
|
||||||
}(cc)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
|
@ -100,30 +100,38 @@ func (w *testWatcher) inject(updates []*naming.Update) {
|
|||||||
|
|
||||||
type testNameResolver struct {
|
type testNameResolver struct {
|
||||||
w *testWatcher
|
w *testWatcher
|
||||||
addr string
|
addrs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *testNameResolver) Resolve(target string) (naming.Watcher, error) {
|
func (r *testNameResolver) Resolve(target string) (naming.Watcher, error) {
|
||||||
r.w = &testWatcher{
|
r.w = &testWatcher{
|
||||||
update: make(chan *naming.Update, 1),
|
update: make(chan *naming.Update, len(r.addrs)),
|
||||||
side: make(chan int, 1),
|
side: make(chan int, 1),
|
||||||
readDone: make(chan int),
|
readDone: make(chan int),
|
||||||
}
|
}
|
||||||
r.w.side <- 1
|
r.w.side <- len(r.addrs)
|
||||||
|
for _, addr := range r.addrs {
|
||||||
r.w.update <- &naming.Update{
|
r.w.update <- &naming.Update{
|
||||||
Op: naming.Add,
|
Op: naming.Add,
|
||||||
Addr: r.addr,
|
Addr: addr,
|
||||||
Metadata: &Metadata{
|
Metadata: &Metadata{
|
||||||
AddrType: GRPCLB,
|
AddrType: GRPCLB,
|
||||||
ServerName: lbsn,
|
ServerName: lbsn,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
<-r.w.readDone
|
<-r.w.readDone
|
||||||
}()
|
}()
|
||||||
return r.w, nil
|
return r.w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *testNameResolver) inject(updates []*naming.Update) {
|
||||||
|
if r.w != nil {
|
||||||
|
r.w.inject(updates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type serverNameCheckCreds struct {
|
type serverNameCheckCreds struct {
|
||||||
expected string
|
expected string
|
||||||
sn string
|
sn string
|
||||||
@ -212,6 +220,7 @@ func (b *remoteBalancer) BalanceLoad(stream lbpb.LoadBalancer_BalanceLoadServer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
type helloServer struct {
|
type helloServer struct {
|
||||||
|
addr string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *helloServer) SayHello(ctx context.Context, in *hwpb.HelloRequest) (*hwpb.HelloReply, error) {
|
func (s *helloServer) SayHello(ctx context.Context, in *hwpb.HelloRequest) (*hwpb.HelloReply, error) {
|
||||||
@ -223,17 +232,17 @@ func (s *helloServer) SayHello(ctx context.Context, in *hwpb.HelloRequest) (*hwp
|
|||||||
return nil, grpc.Errorf(codes.Internal, "received unexpected metadata: %v", md)
|
return nil, grpc.Errorf(codes.Internal, "received unexpected metadata: %v", md)
|
||||||
}
|
}
|
||||||
return &hwpb.HelloReply{
|
return &hwpb.HelloReply{
|
||||||
Message: "Hello " + in.Name,
|
Message: "Hello " + in.Name + " for " + s.addr,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func startBackends(t *testing.T, sn string, lis ...net.Listener) (servers []*grpc.Server) {
|
func startBackends(sn string, lis ...net.Listener) (servers []*grpc.Server) {
|
||||||
for _, l := range lis {
|
for _, l := range lis {
|
||||||
creds := &serverNameCheckCreds{
|
creds := &serverNameCheckCreds{
|
||||||
sn: sn,
|
sn: sn,
|
||||||
}
|
}
|
||||||
s := grpc.NewServer(grpc.Creds(creds))
|
s := grpc.NewServer(grpc.Creds(creds))
|
||||||
hwpb.RegisterGreeterServer(s, &helloServer{})
|
hwpb.RegisterGreeterServer(s, &helloServer{addr: l.Addr().String()})
|
||||||
servers = append(servers, s)
|
servers = append(servers, s)
|
||||||
go func(s *grpc.Server, l net.Listener) {
|
go func(s *grpc.Server, l net.Listener) {
|
||||||
s.Serve(l)
|
s.Serve(l)
|
||||||
@ -248,32 +257,86 @@ func stopBackends(servers []*grpc.Server) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGRPCLB(t *testing.T) {
|
type testServers struct {
|
||||||
|
lbAddr string
|
||||||
|
ls *remoteBalancer
|
||||||
|
lb *grpc.Server
|
||||||
|
beIPs []net.IP
|
||||||
|
bePorts []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLoadBalancer(numberOfBackends int) (tss *testServers, cleanup func(), err error) {
|
||||||
|
var (
|
||||||
|
beListeners []net.Listener
|
||||||
|
ls *remoteBalancer
|
||||||
|
lb *grpc.Server
|
||||||
|
beIPs []net.IP
|
||||||
|
bePorts []int
|
||||||
|
)
|
||||||
|
for i := 0; i < numberOfBackends; i++ {
|
||||||
// Start a backend.
|
// Start a backend.
|
||||||
beLis, err := net.Listen("tcp", "localhost:0")
|
beLis, e := net.Listen("tcp", "localhost:0")
|
||||||
if err != nil {
|
if e != nil {
|
||||||
t.Fatalf("Failed to listen %v", err)
|
err = fmt.Errorf("Failed to listen %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
beIPs = append(beIPs, beLis.Addr().(*net.TCPAddr).IP)
|
||||||
|
|
||||||
beAddr := strings.Split(beLis.Addr().String(), ":")
|
beAddr := strings.Split(beLis.Addr().String(), ":")
|
||||||
bePort, err := strconv.Atoi(beAddr[1])
|
bePort, _ := strconv.Atoi(beAddr[1])
|
||||||
backends := startBackends(t, besn, beLis)
|
bePorts = append(bePorts, bePort)
|
||||||
defer stopBackends(backends)
|
|
||||||
|
beListeners = append(beListeners, beLis)
|
||||||
|
}
|
||||||
|
backends := startBackends(besn, beListeners...)
|
||||||
|
|
||||||
// Start a load balancer.
|
// Start a load balancer.
|
||||||
lbLis, err := net.Listen("tcp", "localhost:0")
|
lbLis, err := net.Listen("tcp", "localhost:0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create the listener for the load balancer %v", err)
|
err = fmt.Errorf("Failed to create the listener for the load balancer %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
lbCreds := &serverNameCheckCreds{
|
lbCreds := &serverNameCheckCreds{
|
||||||
sn: lbsn,
|
sn: lbsn,
|
||||||
}
|
}
|
||||||
lb := grpc.NewServer(grpc.Creds(lbCreds))
|
lb = grpc.NewServer(grpc.Creds(lbCreds))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to generate the port number %v", err)
|
err = fmt.Errorf("Failed to generate the port number %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ls = newRemoteBalancer(nil, nil)
|
||||||
|
lbpb.RegisterLoadBalancerServer(lb, ls)
|
||||||
|
go func() {
|
||||||
|
lb.Serve(lbLis)
|
||||||
|
}()
|
||||||
|
|
||||||
|
tss = &testServers{
|
||||||
|
lbAddr: lbLis.Addr().String(),
|
||||||
|
ls: ls,
|
||||||
|
lb: lb,
|
||||||
|
beIPs: beIPs,
|
||||||
|
bePorts: bePorts,
|
||||||
|
}
|
||||||
|
cleanup = func() {
|
||||||
|
defer stopBackends(backends)
|
||||||
|
defer func() {
|
||||||
|
ls.stop()
|
||||||
|
lb.Stop()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGRPCLB(t *testing.T) {
|
||||||
|
tss, cleanup, err := newLoadBalancer(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create new load balancer: %v", err)
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
be := &lbpb.Server{
|
be := &lbpb.Server{
|
||||||
IpAddress: beLis.Addr().(*net.TCPAddr).IP,
|
IpAddress: tss.beIPs[0],
|
||||||
Port: int32(bePort),
|
Port: int32(tss.bePorts[0]),
|
||||||
LoadBalanceToken: lbToken,
|
LoadBalanceToken: lbToken,
|
||||||
}
|
}
|
||||||
var bes []*lbpb.Server
|
var bes []*lbpb.Server
|
||||||
@ -281,23 +344,14 @@ func TestGRPCLB(t *testing.T) {
|
|||||||
sl := &lbpb.ServerList{
|
sl := &lbpb.ServerList{
|
||||||
Servers: bes,
|
Servers: bes,
|
||||||
}
|
}
|
||||||
sls := []*lbpb.ServerList{sl}
|
tss.ls.sls = []*lbpb.ServerList{sl}
|
||||||
intervals := []time.Duration{0}
|
tss.ls.intervals = []time.Duration{0}
|
||||||
ls := newRemoteBalancer(sls, intervals)
|
|
||||||
lbpb.RegisterLoadBalancerServer(lb, ls)
|
|
||||||
go func() {
|
|
||||||
lb.Serve(lbLis)
|
|
||||||
}()
|
|
||||||
defer func() {
|
|
||||||
ls.stop()
|
|
||||||
lb.Stop()
|
|
||||||
}()
|
|
||||||
creds := serverNameCheckCreds{
|
creds := serverNameCheckCreds{
|
||||||
expected: besn,
|
expected: besn,
|
||||||
}
|
}
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{
|
cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{
|
||||||
addr: lbLis.Addr().String(),
|
addrs: []string{tss.lbAddr},
|
||||||
})), grpc.WithBlock(), grpc.WithTransportCredentials(&creds))
|
})), grpc.WithBlock(), grpc.WithTransportCredentials(&creds))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to dial to the backend %v", err)
|
t.Fatalf("Failed to dial to the backend %v", err)
|
||||||
@ -310,65 +364,31 @@ func TestGRPCLB(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDropRequest(t *testing.T) {
|
func TestDropRequest(t *testing.T) {
|
||||||
// Start 2 backends.
|
tss, cleanup, err := newLoadBalancer(2)
|
||||||
beLis1, err := net.Listen("tcp", "localhost:0")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to listen %v", err)
|
t.Fatalf("failed to create new load balancer: %v", err)
|
||||||
}
|
}
|
||||||
beAddr1 := strings.Split(beLis1.Addr().String(), ":")
|
defer cleanup()
|
||||||
bePort1, err := strconv.Atoi(beAddr1[1])
|
tss.ls.sls = []*lbpb.ServerList{{
|
||||||
|
|
||||||
beLis2, err := net.Listen("tcp", "localhost:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to listen %v", err)
|
|
||||||
}
|
|
||||||
beAddr2 := strings.Split(beLis2.Addr().String(), ":")
|
|
||||||
bePort2, err := strconv.Atoi(beAddr2[1])
|
|
||||||
|
|
||||||
backends := startBackends(t, besn, beLis1, beLis2)
|
|
||||||
defer stopBackends(backends)
|
|
||||||
|
|
||||||
// Start a load balancer.
|
|
||||||
lbLis, err := net.Listen("tcp", "localhost:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create the listener for the load balancer %v", err)
|
|
||||||
}
|
|
||||||
lbCreds := &serverNameCheckCreds{
|
|
||||||
sn: lbsn,
|
|
||||||
}
|
|
||||||
lb := grpc.NewServer(grpc.Creds(lbCreds))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate the port number %v", err)
|
|
||||||
}
|
|
||||||
sls := []*lbpb.ServerList{{
|
|
||||||
Servers: []*lbpb.Server{{
|
Servers: []*lbpb.Server{{
|
||||||
IpAddress: beLis1.Addr().(*net.TCPAddr).IP,
|
IpAddress: tss.beIPs[0],
|
||||||
Port: int32(bePort1),
|
Port: int32(tss.bePorts[0]),
|
||||||
LoadBalanceToken: lbToken,
|
LoadBalanceToken: lbToken,
|
||||||
DropRequest: true,
|
DropRequest: true,
|
||||||
}, {
|
}, {
|
||||||
IpAddress: beLis2.Addr().(*net.TCPAddr).IP,
|
IpAddress: tss.beIPs[1],
|
||||||
Port: int32(bePort2),
|
Port: int32(tss.bePorts[1]),
|
||||||
LoadBalanceToken: lbToken,
|
LoadBalanceToken: lbToken,
|
||||||
DropRequest: false,
|
DropRequest: false,
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
intervals := []time.Duration{0}
|
tss.ls.intervals = []time.Duration{0}
|
||||||
ls := newRemoteBalancer(sls, intervals)
|
|
||||||
lbpb.RegisterLoadBalancerServer(lb, ls)
|
|
||||||
go func() {
|
|
||||||
lb.Serve(lbLis)
|
|
||||||
}()
|
|
||||||
defer func() {
|
|
||||||
ls.stop()
|
|
||||||
lb.Stop()
|
|
||||||
}()
|
|
||||||
creds := serverNameCheckCreds{
|
creds := serverNameCheckCreds{
|
||||||
expected: besn,
|
expected: besn,
|
||||||
}
|
}
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{
|
cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{
|
||||||
addr: lbLis.Addr().String(),
|
addrs: []string{tss.lbAddr},
|
||||||
})), grpc.WithBlock(), grpc.WithTransportCredentials(&creds))
|
})), grpc.WithBlock(), grpc.WithTransportCredentials(&creds))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to dial to the backend %v", err)
|
t.Fatalf("Failed to dial to the backend %v", err)
|
||||||
@ -395,31 +415,14 @@ func TestDropRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDropRequestFailedNonFailFast(t *testing.T) {
|
func TestDropRequestFailedNonFailFast(t *testing.T) {
|
||||||
// Start a backend.
|
tss, cleanup, err := newLoadBalancer(1)
|
||||||
beLis, err := net.Listen("tcp", "localhost:0")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to listen %v", err)
|
t.Fatalf("failed to create new load balancer: %v", err)
|
||||||
}
|
|
||||||
beAddr := strings.Split(beLis.Addr().String(), ":")
|
|
||||||
bePort, err := strconv.Atoi(beAddr[1])
|
|
||||||
backends := startBackends(t, besn, beLis)
|
|
||||||
defer stopBackends(backends)
|
|
||||||
|
|
||||||
// Start a load balancer.
|
|
||||||
lbLis, err := net.Listen("tcp", "localhost:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create the listener for the load balancer %v", err)
|
|
||||||
}
|
|
||||||
lbCreds := &serverNameCheckCreds{
|
|
||||||
sn: lbsn,
|
|
||||||
}
|
|
||||||
lb := grpc.NewServer(grpc.Creds(lbCreds))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate the port number %v", err)
|
|
||||||
}
|
}
|
||||||
|
defer cleanup()
|
||||||
be := &lbpb.Server{
|
be := &lbpb.Server{
|
||||||
IpAddress: beLis.Addr().(*net.TCPAddr).IP,
|
IpAddress: tss.beIPs[0],
|
||||||
Port: int32(bePort),
|
Port: int32(tss.bePorts[0]),
|
||||||
LoadBalanceToken: lbToken,
|
LoadBalanceToken: lbToken,
|
||||||
DropRequest: true,
|
DropRequest: true,
|
||||||
}
|
}
|
||||||
@ -428,23 +431,14 @@ func TestDropRequestFailedNonFailFast(t *testing.T) {
|
|||||||
sl := &lbpb.ServerList{
|
sl := &lbpb.ServerList{
|
||||||
Servers: bes,
|
Servers: bes,
|
||||||
}
|
}
|
||||||
sls := []*lbpb.ServerList{sl}
|
tss.ls.sls = []*lbpb.ServerList{sl}
|
||||||
intervals := []time.Duration{0}
|
tss.ls.intervals = []time.Duration{0}
|
||||||
ls := newRemoteBalancer(sls, intervals)
|
|
||||||
lbpb.RegisterLoadBalancerServer(lb, ls)
|
|
||||||
go func() {
|
|
||||||
lb.Serve(lbLis)
|
|
||||||
}()
|
|
||||||
defer func() {
|
|
||||||
ls.stop()
|
|
||||||
lb.Stop()
|
|
||||||
}()
|
|
||||||
creds := serverNameCheckCreds{
|
creds := serverNameCheckCreds{
|
||||||
expected: besn,
|
expected: besn,
|
||||||
}
|
}
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{
|
cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{
|
||||||
addr: lbLis.Addr().String(),
|
addrs: []string{tss.lbAddr},
|
||||||
})), grpc.WithBlock(), grpc.WithTransportCredentials(&creds))
|
})), grpc.WithBlock(), grpc.WithTransportCredentials(&creds))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to dial to the backend %v", err)
|
t.Fatalf("Failed to dial to the backend %v", err)
|
||||||
@ -458,31 +452,14 @@ func TestDropRequestFailedNonFailFast(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServerExpiration(t *testing.T) {
|
func TestServerExpiration(t *testing.T) {
|
||||||
// Start a backend.
|
tss, cleanup, err := newLoadBalancer(1)
|
||||||
beLis, err := net.Listen("tcp", "localhost:0")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to listen %v", err)
|
t.Fatalf("failed to create new load balancer: %v", err)
|
||||||
}
|
|
||||||
beAddr := strings.Split(beLis.Addr().String(), ":")
|
|
||||||
bePort, err := strconv.Atoi(beAddr[1])
|
|
||||||
backends := startBackends(t, besn, beLis)
|
|
||||||
defer stopBackends(backends)
|
|
||||||
|
|
||||||
// Start a load balancer.
|
|
||||||
lbLis, err := net.Listen("tcp", "localhost:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create the listener for the load balancer %v", err)
|
|
||||||
}
|
|
||||||
lbCreds := &serverNameCheckCreds{
|
|
||||||
sn: lbsn,
|
|
||||||
}
|
|
||||||
lb := grpc.NewServer(grpc.Creds(lbCreds))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate the port number %v", err)
|
|
||||||
}
|
}
|
||||||
|
defer cleanup()
|
||||||
be := &lbpb.Server{
|
be := &lbpb.Server{
|
||||||
IpAddress: beLis.Addr().(*net.TCPAddr).IP,
|
IpAddress: tss.beIPs[0],
|
||||||
Port: int32(bePort),
|
Port: int32(tss.bePorts[0]),
|
||||||
LoadBalanceToken: lbToken,
|
LoadBalanceToken: lbToken,
|
||||||
}
|
}
|
||||||
var bes []*lbpb.Server
|
var bes []*lbpb.Server
|
||||||
@ -504,21 +481,14 @@ func TestServerExpiration(t *testing.T) {
|
|||||||
var intervals []time.Duration
|
var intervals []time.Duration
|
||||||
intervals = append(intervals, 0)
|
intervals = append(intervals, 0)
|
||||||
intervals = append(intervals, 500*time.Millisecond)
|
intervals = append(intervals, 500*time.Millisecond)
|
||||||
ls := newRemoteBalancer(sls, intervals)
|
tss.ls.sls = sls
|
||||||
lbpb.RegisterLoadBalancerServer(lb, ls)
|
tss.ls.intervals = intervals
|
||||||
go func() {
|
|
||||||
lb.Serve(lbLis)
|
|
||||||
}()
|
|
||||||
defer func() {
|
|
||||||
ls.stop()
|
|
||||||
lb.Stop()
|
|
||||||
}()
|
|
||||||
creds := serverNameCheckCreds{
|
creds := serverNameCheckCreds{
|
||||||
expected: besn,
|
expected: besn,
|
||||||
}
|
}
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{
|
cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{
|
||||||
addr: lbLis.Addr().String(),
|
addrs: []string{tss.lbAddr},
|
||||||
})), grpc.WithBlock(), grpc.WithTransportCredentials(&creds))
|
})), grpc.WithBlock(), grpc.WithTransportCredentials(&creds))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to dial to the backend %v", err)
|
t.Fatalf("Failed to dial to the backend %v", err)
|
||||||
@ -539,3 +509,90 @@ func TestServerExpiration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cc.Close()
|
cc.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When the balancer in use disconnects, grpclb should connect to the next address from resolved balancer address list.
|
||||||
|
func TestBalancerDisconnects(t *testing.T) {
|
||||||
|
var (
|
||||||
|
lbAddrs []string
|
||||||
|
lbs []*grpc.Server
|
||||||
|
)
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
tss, cleanup, err := newLoadBalancer(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create new load balancer: %v", err)
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
be := &lbpb.Server{
|
||||||
|
IpAddress: tss.beIPs[0],
|
||||||
|
Port: int32(tss.bePorts[0]),
|
||||||
|
LoadBalanceToken: lbToken,
|
||||||
|
}
|
||||||
|
var bes []*lbpb.Server
|
||||||
|
bes = append(bes, be)
|
||||||
|
sl := &lbpb.ServerList{
|
||||||
|
Servers: bes,
|
||||||
|
}
|
||||||
|
tss.ls.sls = []*lbpb.ServerList{sl}
|
||||||
|
tss.ls.intervals = []time.Duration{0}
|
||||||
|
|
||||||
|
lbAddrs = append(lbAddrs, tss.lbAddr)
|
||||||
|
lbs = append(lbs, tss.lb)
|
||||||
|
}
|
||||||
|
|
||||||
|
creds := serverNameCheckCreds{
|
||||||
|
expected: besn,
|
||||||
|
}
|
||||||
|
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
resolver := &testNameResolver{
|
||||||
|
addrs: lbAddrs[:2],
|
||||||
|
}
|
||||||
|
cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(resolver)), grpc.WithBlock(), grpc.WithTransportCredentials(&creds))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to dial to the backend %v", err)
|
||||||
|
}
|
||||||
|
helloC := hwpb.NewGreeterClient(cc)
|
||||||
|
var message string
|
||||||
|
if resp, err := helloC.SayHello(context.Background(), &hwpb.HelloRequest{Name: "grpc"}); err != nil {
|
||||||
|
t.Fatalf("%v.SayHello(_, _) = _, %v, want _, <nil>", helloC, err)
|
||||||
|
} else {
|
||||||
|
message = resp.Message
|
||||||
|
}
|
||||||
|
// The initial resolver update contains lbs[0] and lbs[1].
|
||||||
|
// When lbs[0] is stopped, lbs[1] should be used.
|
||||||
|
lbs[0].Stop()
|
||||||
|
for {
|
||||||
|
if resp, err := helloC.SayHello(context.Background(), &hwpb.HelloRequest{Name: "grpc"}); err != nil {
|
||||||
|
t.Fatalf("%v.SayHello(_, _) = _, %v, want _, <nil>", helloC, err)
|
||||||
|
} else if resp.Message != message {
|
||||||
|
// A new backend server should receive the request.
|
||||||
|
// The response contains the backend address, so the message should be different from the previous one.
|
||||||
|
message = resp.Message
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
// Inject a update to add lbs[2] to resolved addresses.
|
||||||
|
resolver.inject([]*naming.Update{
|
||||||
|
{Op: naming.Add,
|
||||||
|
Addr: lbAddrs[2],
|
||||||
|
Metadata: &Metadata{
|
||||||
|
AddrType: GRPCLB,
|
||||||
|
ServerName: lbsn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// Stop lbs[1]. Now lbs[0] and lbs[1] are all stopped. lbs[2] should be used.
|
||||||
|
lbs[1].Stop()
|
||||||
|
for {
|
||||||
|
if resp, err := helloC.SayHello(context.Background(), &hwpb.HelloRequest{Name: "grpc"}); err != nil {
|
||||||
|
t.Fatalf("%v.SayHello(_, _) = _, %v, want _, <nil>", helloC, err)
|
||||||
|
} else if resp.Message != message {
|
||||||
|
// A new backend server should receive the request.
|
||||||
|
// The response contains the backend address, so the message should be different from the previous one.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
cc.Close()
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user