github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/Godeps/_workspace/src/google.golang.org/grpc/picker.go (about)

     1  /*
     2   *
     3   * Copyright 2014, Google Inc.
     4   * All rights reserved.
     5   *
     6   * Redistribution and use in source and binary forms, with or without
     7   * modification, are permitted provided that the following conditions are
     8   * met:
     9   *
    10   *     * Redistributions of source code must retain the above copyright
    11   * notice, this list of conditions and the following disclaimer.
    12   *     * Redistributions in binary form must reproduce the above
    13   * copyright notice, this list of conditions and the following disclaimer
    14   * in the documentation and/or other materials provided with the
    15   * distribution.
    16   *     * Neither the name of Google Inc. nor the names of its
    17   * contributors may be used to endorse or promote products derived from
    18   * this software without specific prior written permission.
    19   *
    20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    21   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    22   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    23   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    24   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    25   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    26   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    28   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    29   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    30   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    31   *
    32   */
    33  
    34  package grpc
    35  
    36  import (
    37  	"container/list"
    38  	"fmt"
    39  	"sync"
    40  
    41  	"golang.org/x/net/context"
    42  	"google.golang.org/grpc/grpclog"
    43  	"google.golang.org/grpc/naming"
    44  	"google.golang.org/grpc/transport"
    45  )
    46  
    47  // Picker picks a Conn for RPC requests.
    48  // This is EXPERIMENTAL and please do not implement your own Picker for now.
    49  type Picker interface {
    50  	// Init does initial processing for the Picker, e.g., initiate some connections.
    51  	Init(cc *ClientConn) error
    52  	// Pick blocks until either a transport.ClientTransport is ready for the upcoming RPC
    53  	// or some error happens.
    54  	Pick(ctx context.Context) (transport.ClientTransport, error)
    55  	// PickAddr picks a peer address for connecting. This will be called repeated for
    56  	// connecting/reconnecting.
    57  	PickAddr() (string, error)
    58  	// State returns the connectivity state of the underlying connections.
    59  	State() (ConnectivityState, error)
    60  	// WaitForStateChange blocks until the state changes to something other than
    61  	// the sourceState. It returns the new state or error.
    62  	WaitForStateChange(ctx context.Context, sourceState ConnectivityState) (ConnectivityState, error)
    63  	// Close closes all the Conn's owned by this Picker.
    64  	Close() error
    65  }
    66  
    67  // unicastPicker is the default Picker which is used when there is no custom Picker
    68  // specified by users. It always picks the same Conn.
    69  type unicastPicker struct {
    70  	target string
    71  	conn   *Conn
    72  }
    73  
    74  func (p *unicastPicker) Init(cc *ClientConn) error {
    75  	c, err := NewConn(cc)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	p.conn = c
    80  	return nil
    81  }
    82  
    83  func (p *unicastPicker) Pick(ctx context.Context) (transport.ClientTransport, error) {
    84  	return p.conn.Wait(ctx)
    85  }
    86  
    87  func (p *unicastPicker) PickAddr() (string, error) {
    88  	return p.target, nil
    89  }
    90  
    91  func (p *unicastPicker) State() (ConnectivityState, error) {
    92  	return p.conn.State(), nil
    93  }
    94  
    95  func (p *unicastPicker) WaitForStateChange(ctx context.Context, sourceState ConnectivityState) (ConnectivityState, error) {
    96  	return p.conn.WaitForStateChange(ctx, sourceState)
    97  }
    98  
    99  func (p *unicastPicker) Close() error {
   100  	if p.conn != nil {
   101  		return p.conn.Close()
   102  	}
   103  	return nil
   104  }
   105  
   106  // unicastNamingPicker picks an address from a name resolver to set up the connection.
   107  type unicastNamingPicker struct {
   108  	cc       *ClientConn
   109  	resolver naming.Resolver
   110  	watcher  naming.Watcher
   111  	mu       sync.Mutex
   112  	// The list of the addresses are obtained from watcher.
   113  	addrs *list.List
   114  	// It tracks the current picked addr by PickAddr(). The next PickAddr may
   115  	// push it forward on addrs.
   116  	pickedAddr *list.Element
   117  	conn       *Conn
   118  }
   119  
   120  // NewUnicastNamingPicker creates a Picker to pick addresses from a name resolver
   121  // to connect.
   122  func NewUnicastNamingPicker(r naming.Resolver) Picker {
   123  	return &unicastNamingPicker{
   124  		resolver: r,
   125  		addrs:    list.New(),
   126  	}
   127  }
   128  
   129  type addrInfo struct {
   130  	addr string
   131  	// Set to true if this addrInfo needs to be deleted in the next PickAddrr() call.
   132  	deleting bool
   133  }
   134  
   135  // processUpdates calls Watcher.Next() once and processes the obtained updates.
   136  func (p *unicastNamingPicker) processUpdates() error {
   137  	updates, err := p.watcher.Next()
   138  	if err != nil {
   139  		return err
   140  	}
   141  	for _, update := range updates {
   142  		switch update.Op {
   143  		case naming.Add:
   144  			p.mu.Lock()
   145  			p.addrs.PushBack(&addrInfo{
   146  				addr: update.Addr,
   147  			})
   148  			p.mu.Unlock()
   149  			// Initial connection setup
   150  			if p.conn == nil {
   151  				conn, err := NewConn(p.cc)
   152  				if err != nil {
   153  					return err
   154  				}
   155  				p.conn = conn
   156  			}
   157  		case naming.Delete:
   158  			p.mu.Lock()
   159  			for e := p.addrs.Front(); e != nil; e = e.Next() {
   160  				if update.Addr == e.Value.(*addrInfo).addr {
   161  					if e == p.pickedAddr {
   162  						// Do not remove the element now if it is the current picked
   163  						// one. We leave the deletion to the next PickAddr() call.
   164  						e.Value.(*addrInfo).deleting = true
   165  						// Notify Conn to close it. All the live RPCs on this connection
   166  						// will be aborted.
   167  						p.conn.NotifyReset()
   168  					} else {
   169  						p.addrs.Remove(e)
   170  					}
   171  				}
   172  			}
   173  			p.mu.Unlock()
   174  		default:
   175  			grpclog.Println("Unknown update.Op ", update.Op)
   176  		}
   177  	}
   178  	return nil
   179  }
   180  
   181  // monitor runs in a standalone goroutine to keep watching name resolution updates until the watcher
   182  // is closed.
   183  func (p *unicastNamingPicker) monitor() {
   184  	for {
   185  		if err := p.processUpdates(); err != nil {
   186  			return
   187  		}
   188  	}
   189  }
   190  
   191  func (p *unicastNamingPicker) Init(cc *ClientConn) error {
   192  	w, err := p.resolver.Resolve(cc.target)
   193  	if err != nil {
   194  		return err
   195  	}
   196  	p.watcher = w
   197  	p.cc = cc
   198  	// Get the initial name resolution.
   199  	if err := p.processUpdates(); err != nil {
   200  		return err
   201  	}
   202  	go p.monitor()
   203  	return nil
   204  }
   205  
   206  func (p *unicastNamingPicker) Pick(ctx context.Context) (transport.ClientTransport, error) {
   207  	return p.conn.Wait(ctx)
   208  }
   209  
   210  func (p *unicastNamingPicker) PickAddr() (string, error) {
   211  	p.mu.Lock()
   212  	defer p.mu.Unlock()
   213  	if p.pickedAddr == nil {
   214  		p.pickedAddr = p.addrs.Front()
   215  	} else {
   216  		pa := p.pickedAddr
   217  		p.pickedAddr = pa.Next()
   218  		if pa.Value.(*addrInfo).deleting {
   219  			p.addrs.Remove(pa)
   220  		}
   221  		if p.pickedAddr == nil {
   222  			p.pickedAddr = p.addrs.Front()
   223  		}
   224  	}
   225  	if p.pickedAddr == nil {
   226  		return "", fmt.Errorf("there is no address available to pick")
   227  	}
   228  	return p.pickedAddr.Value.(*addrInfo).addr, nil
   229  }
   230  
   231  func (p *unicastNamingPicker) State() (ConnectivityState, error) {
   232  	return 0, fmt.Errorf("State() is not supported for unicastNamingPicker")
   233  }
   234  
   235  func (p *unicastNamingPicker) WaitForStateChange(ctx context.Context, sourceState ConnectivityState) (ConnectivityState, error) {
   236  	return 0, fmt.Errorf("WaitForStateChange is not supported for unicastNamingPciker")
   237  }
   238  
   239  func (p *unicastNamingPicker) Close() error {
   240  	p.watcher.Close()
   241  	p.conn.Close()
   242  	return nil
   243  }