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:
Menghan Li
2017-04-13 13:41:35 -07:00
committed by GitHub
parent 0e8b58d22f
commit 8a6eb0f6e9
2 changed files with 298 additions and 187 deletions

View File

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

View File

@ -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()
}