2017-01-20 16:59:14 +01:00
|
|
|
/*
|
|
|
|
*
|
2017-08-11 19:19:46 +02:00
|
|
|
* Copyright 2016 gRPC authors.
|
2017-01-20 16:59:14 +01:00
|
|
|
*
|
2017-08-11 19:19:46 +02:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2017-01-20 16:59:14 +01:00
|
|
|
*
|
2017-08-11 19:19:46 +02:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2017-01-20 16:59:14 +01:00
|
|
|
*
|
2017-08-11 19:19:46 +02:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2017-01-20 16:59:14 +01:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
package grpc
|
|
|
|
|
|
|
|
import (
|
2019-02-20 19:51:33 +01:00
|
|
|
"context"
|
2017-08-11 19:19:46 +02:00
|
|
|
"net"
|
2017-01-20 16:59:14 +01:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/credentials"
|
|
|
|
"google.golang.org/grpc/grpclog"
|
|
|
|
"google.golang.org/grpc/naming"
|
2018-09-24 18:30:39 +02:00
|
|
|
"google.golang.org/grpc/status"
|
2017-01-20 16:59:14 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// Address represents a server the client connects to.
|
2018-09-24 18:30:39 +02:00
|
|
|
//
|
|
|
|
// Deprecated: please use package balancer.
|
2017-01-20 16:59:14 +01:00
|
|
|
type Address struct {
|
|
|
|
// Addr is the server address on which a connection will be established.
|
|
|
|
Addr string
|
|
|
|
// Metadata is the information associated with Addr, which may be used
|
|
|
|
// to make load balancing decision.
|
|
|
|
Metadata interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// BalancerConfig specifies the configurations for Balancer.
|
2018-09-24 18:30:39 +02:00
|
|
|
//
|
command: Unmanaged providers
This adds supports for "unmanaged" providers, or providers with process
lifecycles not controlled by Terraform. These providers are assumed to
be started before Terraform is launched, and are assumed to shut
themselves down after Terraform has finished running.
To do this, we must update the go-plugin dependency to v1.3.0, which
added support for the "test mode" plugin serving that powers all this.
As a side-effect of not needing to manage the process lifecycle anymore,
Terraform also no longer needs to worry about the provider's binary, as
it won't be used for anything anymore. Because of this, we can disable
the init behavior that concerns itself with downloading that provider's
binary, checking its version, and otherwise managing the binary.
This is all managed on a per-provider basis, so managed providers that
Terraform downloads, starts, and stops can be used in the same commands
as unmanaged providers. The TF_REATTACH_PROVIDERS environment variable
is added, and is a JSON encoding of the provider's address to the
information we need to connect to it.
This change enables two benefits: first, delve and other debuggers can
now be attached to provider server processes, and Terraform can connect.
This allows for attaching debuggers to provider processes, which before
was difficult to impossible. Second, it allows the SDK test framework to
host the provider in the same process as the test driver, while running
a production Terraform binary against the provider. This allows for Go's
built-in race detector and test coverage tooling to work as expected in
provider tests.
Unmanaged providers are expected to work in the exact same way as
managed providers, with one caveat: Terraform kills provider processes
and restarts them once per graph walk, meaning multiple times during
most Terraform CLI commands. As unmanaged providers can't be killed by
Terraform, and have no visibility into graph walks, unmanaged providers
are likely to have differences in how their global mutable state behaves
when compared to managed providers. Namely, unmanaged providers are
likely to retain global state when managed providers would have reset
it. Developers relying on global state should be aware of this.
2020-05-27 02:48:57 +02:00
|
|
|
// Deprecated: please use package balancer. May be removed in a future 1.x release.
|
2017-01-20 16:59:14 +01:00
|
|
|
type BalancerConfig struct {
|
|
|
|
// DialCreds is the transport credential the Balancer implementation can
|
|
|
|
// use to dial to a remote load balancer server. The Balancer implementations
|
|
|
|
// can ignore this if it does not need to talk to another party securely.
|
|
|
|
DialCreds credentials.TransportCredentials
|
2017-08-11 19:19:46 +02:00
|
|
|
// Dialer is the custom dialer the Balancer implementation can use to dial
|
|
|
|
// to a remote load balancer server. The Balancer implementations
|
|
|
|
// can ignore this if it doesn't need to talk to remote balancer.
|
|
|
|
Dialer func(context.Context, string) (net.Conn, error)
|
2017-01-20 16:59:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// BalancerGetOptions configures a Get call.
|
2018-09-24 18:30:39 +02:00
|
|
|
//
|
command: Unmanaged providers
This adds supports for "unmanaged" providers, or providers with process
lifecycles not controlled by Terraform. These providers are assumed to
be started before Terraform is launched, and are assumed to shut
themselves down after Terraform has finished running.
To do this, we must update the go-plugin dependency to v1.3.0, which
added support for the "test mode" plugin serving that powers all this.
As a side-effect of not needing to manage the process lifecycle anymore,
Terraform also no longer needs to worry about the provider's binary, as
it won't be used for anything anymore. Because of this, we can disable
the init behavior that concerns itself with downloading that provider's
binary, checking its version, and otherwise managing the binary.
This is all managed on a per-provider basis, so managed providers that
Terraform downloads, starts, and stops can be used in the same commands
as unmanaged providers. The TF_REATTACH_PROVIDERS environment variable
is added, and is a JSON encoding of the provider's address to the
information we need to connect to it.
This change enables two benefits: first, delve and other debuggers can
now be attached to provider server processes, and Terraform can connect.
This allows for attaching debuggers to provider processes, which before
was difficult to impossible. Second, it allows the SDK test framework to
host the provider in the same process as the test driver, while running
a production Terraform binary against the provider. This allows for Go's
built-in race detector and test coverage tooling to work as expected in
provider tests.
Unmanaged providers are expected to work in the exact same way as
managed providers, with one caveat: Terraform kills provider processes
and restarts them once per graph walk, meaning multiple times during
most Terraform CLI commands. As unmanaged providers can't be killed by
Terraform, and have no visibility into graph walks, unmanaged providers
are likely to have differences in how their global mutable state behaves
when compared to managed providers. Namely, unmanaged providers are
likely to retain global state when managed providers would have reset
it. Developers relying on global state should be aware of this.
2020-05-27 02:48:57 +02:00
|
|
|
// Deprecated: please use package balancer. May be removed in a future 1.x release.
|
2017-01-20 16:59:14 +01:00
|
|
|
type BalancerGetOptions struct {
|
|
|
|
// BlockingWait specifies whether Get should block when there is no
|
|
|
|
// connected address.
|
|
|
|
BlockingWait bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Balancer chooses network addresses for RPCs.
|
2018-09-24 18:30:39 +02:00
|
|
|
//
|
command: Unmanaged providers
This adds supports for "unmanaged" providers, or providers with process
lifecycles not controlled by Terraform. These providers are assumed to
be started before Terraform is launched, and are assumed to shut
themselves down after Terraform has finished running.
To do this, we must update the go-plugin dependency to v1.3.0, which
added support for the "test mode" plugin serving that powers all this.
As a side-effect of not needing to manage the process lifecycle anymore,
Terraform also no longer needs to worry about the provider's binary, as
it won't be used for anything anymore. Because of this, we can disable
the init behavior that concerns itself with downloading that provider's
binary, checking its version, and otherwise managing the binary.
This is all managed on a per-provider basis, so managed providers that
Terraform downloads, starts, and stops can be used in the same commands
as unmanaged providers. The TF_REATTACH_PROVIDERS environment variable
is added, and is a JSON encoding of the provider's address to the
information we need to connect to it.
This change enables two benefits: first, delve and other debuggers can
now be attached to provider server processes, and Terraform can connect.
This allows for attaching debuggers to provider processes, which before
was difficult to impossible. Second, it allows the SDK test framework to
host the provider in the same process as the test driver, while running
a production Terraform binary against the provider. This allows for Go's
built-in race detector and test coverage tooling to work as expected in
provider tests.
Unmanaged providers are expected to work in the exact same way as
managed providers, with one caveat: Terraform kills provider processes
and restarts them once per graph walk, meaning multiple times during
most Terraform CLI commands. As unmanaged providers can't be killed by
Terraform, and have no visibility into graph walks, unmanaged providers
are likely to have differences in how their global mutable state behaves
when compared to managed providers. Namely, unmanaged providers are
likely to retain global state when managed providers would have reset
it. Developers relying on global state should be aware of this.
2020-05-27 02:48:57 +02:00
|
|
|
// Deprecated: please use package balancer. May be removed in a future 1.x release.
|
2017-01-20 16:59:14 +01:00
|
|
|
type Balancer interface {
|
|
|
|
// Start does the initialization work to bootstrap a Balancer. For example,
|
|
|
|
// this function may start the name resolution and watch the updates. It will
|
|
|
|
// be called when dialing.
|
|
|
|
Start(target string, config BalancerConfig) error
|
|
|
|
// Up informs the Balancer that gRPC has a connection to the server at
|
|
|
|
// addr. It returns down which is called once the connection to addr gets
|
|
|
|
// lost or closed.
|
|
|
|
// TODO: It is not clear how to construct and take advantage of the meaningful error
|
|
|
|
// parameter for down. Need realistic demands to guide.
|
|
|
|
Up(addr Address) (down func(error))
|
|
|
|
// Get gets the address of a server for the RPC corresponding to ctx.
|
|
|
|
// i) If it returns a connected address, gRPC internals issues the RPC on the
|
|
|
|
// connection to this address;
|
|
|
|
// ii) If it returns an address on which the connection is under construction
|
|
|
|
// (initiated by Notify(...)) but not connected, gRPC internals
|
|
|
|
// * fails RPC if the RPC is fail-fast and connection is in the TransientFailure or
|
|
|
|
// Shutdown state;
|
|
|
|
// or
|
|
|
|
// * issues RPC on the connection otherwise.
|
|
|
|
// iii) If it returns an address on which the connection does not exist, gRPC
|
|
|
|
// internals treats it as an error and will fail the corresponding RPC.
|
|
|
|
//
|
|
|
|
// Therefore, the following is the recommended rule when writing a custom Balancer.
|
|
|
|
// If opts.BlockingWait is true, it should return a connected address or
|
|
|
|
// block if there is no connected address. It should respect the timeout or
|
|
|
|
// cancellation of ctx when blocking. If opts.BlockingWait is false (for fail-fast
|
|
|
|
// RPCs), it should return an address it has notified via Notify(...) immediately
|
|
|
|
// instead of blocking.
|
|
|
|
//
|
|
|
|
// The function returns put which is called once the rpc has completed or failed.
|
|
|
|
// put can collect and report RPC stats to a remote load balancer.
|
|
|
|
//
|
|
|
|
// This function should only return the errors Balancer cannot recover by itself.
|
|
|
|
// gRPC internals will fail the RPC if an error is returned.
|
|
|
|
Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error)
|
|
|
|
// Notify returns a channel that is used by gRPC internals to watch the addresses
|
|
|
|
// gRPC needs to connect. The addresses might be from a name resolver or remote
|
|
|
|
// load balancer. gRPC internals will compare it with the existing connected
|
|
|
|
// addresses. If the address Balancer notified is not in the existing connected
|
|
|
|
// addresses, gRPC starts to connect the address. If an address in the existing
|
|
|
|
// connected addresses is not in the notification list, the corresponding connection
|
|
|
|
// is shutdown gracefully. Otherwise, there are no operations to take. Note that
|
|
|
|
// the Address slice must be the full list of the Addresses which should be connected.
|
|
|
|
// It is NOT delta.
|
|
|
|
Notify() <-chan []Address
|
|
|
|
// Close shuts down the balancer.
|
|
|
|
Close() error
|
|
|
|
}
|
|
|
|
|
|
|
|
// RoundRobin returns a Balancer that selects addresses round-robin. It uses r to watch
|
|
|
|
// the name resolution updates and updates the addresses available correspondingly.
|
2018-09-24 18:30:39 +02:00
|
|
|
//
|
command: Unmanaged providers
This adds supports for "unmanaged" providers, or providers with process
lifecycles not controlled by Terraform. These providers are assumed to
be started before Terraform is launched, and are assumed to shut
themselves down after Terraform has finished running.
To do this, we must update the go-plugin dependency to v1.3.0, which
added support for the "test mode" plugin serving that powers all this.
As a side-effect of not needing to manage the process lifecycle anymore,
Terraform also no longer needs to worry about the provider's binary, as
it won't be used for anything anymore. Because of this, we can disable
the init behavior that concerns itself with downloading that provider's
binary, checking its version, and otherwise managing the binary.
This is all managed on a per-provider basis, so managed providers that
Terraform downloads, starts, and stops can be used in the same commands
as unmanaged providers. The TF_REATTACH_PROVIDERS environment variable
is added, and is a JSON encoding of the provider's address to the
information we need to connect to it.
This change enables two benefits: first, delve and other debuggers can
now be attached to provider server processes, and Terraform can connect.
This allows for attaching debuggers to provider processes, which before
was difficult to impossible. Second, it allows the SDK test framework to
host the provider in the same process as the test driver, while running
a production Terraform binary against the provider. This allows for Go's
built-in race detector and test coverage tooling to work as expected in
provider tests.
Unmanaged providers are expected to work in the exact same way as
managed providers, with one caveat: Terraform kills provider processes
and restarts them once per graph walk, meaning multiple times during
most Terraform CLI commands. As unmanaged providers can't be killed by
Terraform, and have no visibility into graph walks, unmanaged providers
are likely to have differences in how their global mutable state behaves
when compared to managed providers. Namely, unmanaged providers are
likely to retain global state when managed providers would have reset
it. Developers relying on global state should be aware of this.
2020-05-27 02:48:57 +02:00
|
|
|
// Deprecated: please use package balancer/roundrobin. May be removed in a future 1.x release.
|
2017-01-20 16:59:14 +01:00
|
|
|
func RoundRobin(r naming.Resolver) Balancer {
|
|
|
|
return &roundRobin{r: r}
|
|
|
|
}
|
|
|
|
|
|
|
|
type addrInfo struct {
|
|
|
|
addr Address
|
|
|
|
connected bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type roundRobin struct {
|
|
|
|
r naming.Resolver
|
|
|
|
w naming.Watcher
|
|
|
|
addrs []*addrInfo // all the addresses the client should potentially connect
|
|
|
|
mu sync.Mutex
|
|
|
|
addrCh chan []Address // the channel to notify gRPC internals the list of addresses the client should connect to.
|
|
|
|
next int // index of the next address to return for Get()
|
|
|
|
waitCh chan struct{} // the channel to block when there is no connected address available
|
|
|
|
done bool // The Balancer is closed.
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rr *roundRobin) watchAddrUpdates() error {
|
|
|
|
updates, err := rr.w.Next()
|
|
|
|
if err != nil {
|
2017-08-11 19:19:46 +02:00
|
|
|
grpclog.Warningf("grpc: the naming watcher stops working due to %v.", err)
|
2017-01-20 16:59:14 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
rr.mu.Lock()
|
|
|
|
defer rr.mu.Unlock()
|
|
|
|
for _, update := range updates {
|
|
|
|
addr := Address{
|
|
|
|
Addr: update.Addr,
|
|
|
|
Metadata: update.Metadata,
|
|
|
|
}
|
|
|
|
switch update.Op {
|
|
|
|
case naming.Add:
|
|
|
|
var exist bool
|
|
|
|
for _, v := range rr.addrs {
|
|
|
|
if addr == v.addr {
|
|
|
|
exist = true
|
2017-08-11 19:19:46 +02:00
|
|
|
grpclog.Infoln("grpc: The name resolver wanted to add an existing address: ", addr)
|
2017-01-20 16:59:14 +01:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if exist {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
rr.addrs = append(rr.addrs, &addrInfo{addr: addr})
|
|
|
|
case naming.Delete:
|
|
|
|
for i, v := range rr.addrs {
|
|
|
|
if addr == v.addr {
|
|
|
|
copy(rr.addrs[i:], rr.addrs[i+1:])
|
|
|
|
rr.addrs = rr.addrs[:len(rr.addrs)-1]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
2017-08-11 19:19:46 +02:00
|
|
|
grpclog.Errorln("Unknown update.Op ", update.Op)
|
2017-01-20 16:59:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Make a copy of rr.addrs and write it onto rr.addrCh so that gRPC internals gets notified.
|
|
|
|
open := make([]Address, len(rr.addrs))
|
|
|
|
for i, v := range rr.addrs {
|
|
|
|
open[i] = v.addr
|
|
|
|
}
|
|
|
|
if rr.done {
|
|
|
|
return ErrClientConnClosing
|
|
|
|
}
|
2017-08-11 19:19:46 +02:00
|
|
|
select {
|
|
|
|
case <-rr.addrCh:
|
|
|
|
default:
|
|
|
|
}
|
2017-01-20 16:59:14 +01:00
|
|
|
rr.addrCh <- open
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rr *roundRobin) Start(target string, config BalancerConfig) error {
|
|
|
|
rr.mu.Lock()
|
|
|
|
defer rr.mu.Unlock()
|
|
|
|
if rr.done {
|
|
|
|
return ErrClientConnClosing
|
|
|
|
}
|
|
|
|
if rr.r == nil {
|
|
|
|
// If there is no name resolver installed, it is not needed to
|
|
|
|
// do name resolution. In this case, target is added into rr.addrs
|
|
|
|
// as the only address available and rr.addrCh stays nil.
|
|
|
|
rr.addrs = append(rr.addrs, &addrInfo{addr: Address{Addr: target}})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
w, err := rr.r.Resolve(target)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
rr.w = w
|
2017-08-11 19:19:46 +02:00
|
|
|
rr.addrCh = make(chan []Address, 1)
|
2017-01-20 16:59:14 +01:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
if err := rr.watchAddrUpdates(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Up sets the connected state of addr and sends notification if there are pending
|
|
|
|
// Get() calls.
|
|
|
|
func (rr *roundRobin) Up(addr Address) func(error) {
|
|
|
|
rr.mu.Lock()
|
|
|
|
defer rr.mu.Unlock()
|
|
|
|
var cnt int
|
|
|
|
for _, a := range rr.addrs {
|
|
|
|
if a.addr == addr {
|
|
|
|
if a.connected {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
a.connected = true
|
|
|
|
}
|
|
|
|
if a.connected {
|
|
|
|
cnt++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// addr is only one which is connected. Notify the Get() callers who are blocking.
|
|
|
|
if cnt == 1 && rr.waitCh != nil {
|
|
|
|
close(rr.waitCh)
|
|
|
|
rr.waitCh = nil
|
|
|
|
}
|
|
|
|
return func(err error) {
|
|
|
|
rr.down(addr, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// down unsets the connected state of addr.
|
|
|
|
func (rr *roundRobin) down(addr Address, err error) {
|
|
|
|
rr.mu.Lock()
|
|
|
|
defer rr.mu.Unlock()
|
|
|
|
for _, a := range rr.addrs {
|
|
|
|
if addr == a.addr {
|
|
|
|
a.connected = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns the next addr in the rotation.
|
|
|
|
func (rr *roundRobin) Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) {
|
|
|
|
var ch chan struct{}
|
|
|
|
rr.mu.Lock()
|
|
|
|
if rr.done {
|
|
|
|
rr.mu.Unlock()
|
|
|
|
err = ErrClientConnClosing
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(rr.addrs) > 0 {
|
|
|
|
if rr.next >= len(rr.addrs) {
|
|
|
|
rr.next = 0
|
|
|
|
}
|
|
|
|
next := rr.next
|
|
|
|
for {
|
|
|
|
a := rr.addrs[next]
|
|
|
|
next = (next + 1) % len(rr.addrs)
|
|
|
|
if a.connected {
|
|
|
|
addr = a.addr
|
|
|
|
rr.next = next
|
|
|
|
rr.mu.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if next == rr.next {
|
|
|
|
// Has iterated all the possible address but none is connected.
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !opts.BlockingWait {
|
|
|
|
if len(rr.addrs) == 0 {
|
|
|
|
rr.mu.Unlock()
|
2018-09-24 18:30:39 +02:00
|
|
|
err = status.Errorf(codes.Unavailable, "there is no address available")
|
2017-01-20 16:59:14 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
// Returns the next addr on rr.addrs for failfast RPCs.
|
|
|
|
addr = rr.addrs[rr.next].addr
|
|
|
|
rr.next++
|
|
|
|
rr.mu.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Wait on rr.waitCh for non-failfast RPCs.
|
|
|
|
if rr.waitCh == nil {
|
|
|
|
ch = make(chan struct{})
|
|
|
|
rr.waitCh = ch
|
|
|
|
} else {
|
|
|
|
ch = rr.waitCh
|
|
|
|
}
|
|
|
|
rr.mu.Unlock()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
err = ctx.Err()
|
|
|
|
return
|
|
|
|
case <-ch:
|
|
|
|
rr.mu.Lock()
|
|
|
|
if rr.done {
|
|
|
|
rr.mu.Unlock()
|
|
|
|
err = ErrClientConnClosing
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(rr.addrs) > 0 {
|
|
|
|
if rr.next >= len(rr.addrs) {
|
|
|
|
rr.next = 0
|
|
|
|
}
|
|
|
|
next := rr.next
|
|
|
|
for {
|
|
|
|
a := rr.addrs[next]
|
|
|
|
next = (next + 1) % len(rr.addrs)
|
|
|
|
if a.connected {
|
|
|
|
addr = a.addr
|
|
|
|
rr.next = next
|
|
|
|
rr.mu.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if next == rr.next {
|
|
|
|
// Has iterated all the possible address but none is connected.
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// The newly added addr got removed by Down() again.
|
|
|
|
if rr.waitCh == nil {
|
|
|
|
ch = make(chan struct{})
|
|
|
|
rr.waitCh = ch
|
|
|
|
} else {
|
|
|
|
ch = rr.waitCh
|
|
|
|
}
|
|
|
|
rr.mu.Unlock()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rr *roundRobin) Notify() <-chan []Address {
|
|
|
|
return rr.addrCh
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rr *roundRobin) Close() error {
|
|
|
|
rr.mu.Lock()
|
|
|
|
defer rr.mu.Unlock()
|
2017-08-11 19:19:46 +02:00
|
|
|
if rr.done {
|
|
|
|
return errBalancerClosed
|
|
|
|
}
|
2017-01-20 16:59:14 +01:00
|
|
|
rr.done = true
|
|
|
|
if rr.w != nil {
|
|
|
|
rr.w.Close()
|
|
|
|
}
|
|
|
|
if rr.waitCh != nil {
|
|
|
|
close(rr.waitCh)
|
|
|
|
rr.waitCh = nil
|
|
|
|
}
|
|
|
|
if rr.addrCh != nil {
|
|
|
|
close(rr.addrCh)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2018-09-24 18:30:39 +02:00
|
|
|
|
|
|
|
// pickFirst is used to test multi-addresses in one addrConn in which all addresses share the same addrConn.
|
|
|
|
// It is a wrapper around roundRobin balancer. The logic of all methods works fine because balancer.Get()
|
|
|
|
// returns the only address Up by resetTransport().
|
|
|
|
type pickFirst struct {
|
|
|
|
*roundRobin
|
|
|
|
}
|