123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- /*
- *
- * Copyright 2016 gRPC authors.
- *
- * 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
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * 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.
- *
- */
- package grpc
- import (
- "fmt"
- "net"
- "sync"
- "golang.org/x/net/context"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/credentials"
- "google.golang.org/grpc/grpclog"
- "google.golang.org/grpc/naming"
- "google.golang.org/grpc/status"
- )
- // Address represents a server the client connects to.
- //
- // Deprecated: please use package balancer.
- 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.
- //
- // Deprecated: please use package balancer.
- 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
- // 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)
- }
- // BalancerGetOptions configures a Get call.
- //
- // Deprecated: please use package balancer.
- type BalancerGetOptions struct {
- // BlockingWait specifies whether Get should block when there is no
- // connected address.
- BlockingWait bool
- }
- // Balancer chooses network addresses for RPCs.
- //
- // Deprecated: please use package balancer.
- 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
- }
- // downErr implements net.Error. It is constructed by gRPC internals and passed to the down
- // call of Balancer.
- type downErr struct {
- timeout bool
- temporary bool
- desc string
- }
- func (e downErr) Error() string { return e.desc }
- func (e downErr) Timeout() bool { return e.timeout }
- func (e downErr) Temporary() bool { return e.temporary }
- func downErrorf(timeout, temporary bool, format string, a ...interface{}) downErr {
- return downErr{
- timeout: timeout,
- temporary: temporary,
- desc: fmt.Sprintf(format, a...),
- }
- }
- // RoundRobin returns a Balancer that selects addresses round-robin. It uses r to watch
- // the name resolution updates and updates the addresses available correspondingly.
- //
- // Deprecated: please use package balancer/roundrobin.
- 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 {
- grpclog.Warningf("grpc: the naming watcher stops working due to %v.", err)
- 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
- grpclog.Infoln("grpc: The name resolver wanted to add an existing address: ", addr)
- 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:
- grpclog.Errorln("Unknown update.Op ", update.Op)
- }
- }
- // 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
- }
- select {
- case <-rr.addrCh:
- default:
- }
- 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
- rr.addrCh = make(chan []Address, 1)
- 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()
- err = status.Errorf(codes.Unavailable, "there is no address available")
- 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()
- if rr.done {
- return errBalancerClosed
- }
- 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
- }
- // 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
- }
- func pickFirstBalancerV1(r naming.Resolver) Balancer {
- return &pickFirst{&roundRobin{r: r}}
- }
|