github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/rpcclientpool/api.go (about)

     1  /*
     2  	Package rpcclientpool wraps net/rpc.Client to manage shared resource pools.
     3  
     4  	Package rpcclientpool wraps net/rpc.Client from the standard library so that
     5  	limited resources (file descriptors used for network connections) may be
     6  	shared easily with other packages such as lib/connpool and lib/srpc.
     7  	A typical programming pattern is:
     8  		cr0 := New(...)
     9  		cr1 := New(...)
    10  		go func() {
    11  			for ... {
    12  				c := cr0.Get(...)
    13  				defer c.Put()
    14  				if err { c.Close() }
    15  			}
    16  		}()
    17  		go func() {
    18  			for ... {
    19  				c := cr1.Get(...)
    20  				defer c.Put()
    21  				if err { c.Close() }
    22  			}
    23  		}()
    24  	This pattern ensures Get and Put are always matched, and if there is a
    25  	communications error, Close shuts down the connection so that a subsequent
    26  	Get	creates a new underlying connection.
    27  
    28  	It is resonable to create one goroutine for each resource, since the Get
    29  	methods will block, waiting for available resources.
    30  */
    31  package rpcclientpool
    32  
    33  import (
    34  	"net/rpc"
    35  
    36  	"github.com/Cloud-Foundations/Dominator/lib/net"
    37  	"github.com/Cloud-Foundations/Dominator/lib/resourcepool"
    38  )
    39  
    40  // Client is a managed RPC client. It implements similar methods as rpc.Client
    41  // from the standard library.
    42  type Client struct {
    43  	rpcClient *rpc.Client
    44  	resource  *ClientResource
    45  }
    46  
    47  func (client *Client) Call(serviceMethod string, args interface{},
    48  	reply interface{}) error {
    49  	return client.rpcClient.Call(serviceMethod, args, reply)
    50  }
    51  
    52  // Close will close the client, immediately freeing the underlying resource.
    53  // It may no longer be used for communication.
    54  func (client *Client) Close() error {
    55  	return client.close()
    56  }
    57  
    58  func (client *Client) Go(serviceMethod string, args interface{},
    59  	reply interface{}, done chan *rpc.Call) *rpc.Call {
    60  	return client.rpcClient.Go(serviceMethod, args, reply, done)
    61  }
    62  
    63  // Put will release the client. It may no longer be used for communication.
    64  // It may be internally closed later if required to free limited resources (such
    65  // as file descriptors). If Put is called after Close, no action is taken (this
    66  // is a safe operation and is commonly used in some programming patterns).
    67  func (client *Client) Put() {
    68  	client.put()
    69  }
    70  
    71  type privateClientResource struct {
    72  	clientResource *ClientResource
    73  }
    74  
    75  type ClientResource struct {
    76  	network               string
    77  	address               string
    78  	http                  bool
    79  	path                  string
    80  	dialer                net.Dialer
    81  	resource              *resourcepool.Resource
    82  	privateClientResource privateClientResource
    83  	client                *Client
    84  	rpcClient             *rpc.Client
    85  }
    86  
    87  // New returns a ClientResource for the specified network address. If a RPC over
    88  // HTTP connection is required then http should be true and the HTTP path should
    89  // be given by path. If path is empty then the default path
    90  // (net/rpc.DefaultRPCPath) is used. In general, the default path should be used.
    91  // The ClientResource may be used later to obtain a Client which is part of a
    92  // managed pool of connection slots (to limit consumption of resources such as
    93  // file descriptors). Clients can be released with the Put method but the
    94  // underlying connection may be kept open for later re-use. The Client is placed
    95  // on an internal list.
    96  func New(network, address string, http bool, path string) *ClientResource {
    97  	return newClientResource(network, address, http, path, defaultDialer)
    98  }
    99  
   100  // NewWithDialer works just like New but allows this resource to use a custom
   101  // dialer.
   102  func NewWithDialer(
   103  	network,
   104  	address string,
   105  	http bool,
   106  	path string,
   107  	dialer net.Dialer) *ClientResource {
   108  	return newClientResource(network, address, http, path, dialer)
   109  }
   110  
   111  // Get will return a Client. Get will wait until a resource is available or a
   112  // message is received on cancelChannel. If cancelChannel is nil then Get will
   113  // wait indefinitely until a resource is available. If the wait is cancelled
   114  // then Get will return ErrorResourceLimitExceeded.
   115  // Get will panic if it is called again without an intervening Close or Put.
   116  func (cr *ClientResource) Get(cancelChannel <-chan struct{}) (*Client, error) {
   117  	return cr.get(cancelChannel)
   118  }