github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/core/comm/producer.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package comm
    18  
    19  import (
    20  	"fmt"
    21  	"math/rand"
    22  	"sync"
    23  
    24  	"github.com/hyperledger/fabric/common/flogging"
    25  	"google.golang.org/grpc"
    26  )
    27  
    28  var logger = flogging.MustGetLogger("ConnProducer")
    29  
    30  // ConnectionFactory creates a connection to a certain endpoint
    31  type ConnectionFactory func(endpoint string) (*grpc.ClientConn, error)
    32  
    33  // ConnectionProducer produces connections out of a set of predefined
    34  // endpoints
    35  type ConnectionProducer interface {
    36  	// NewConnection creates a new connection.
    37  	// Returns the connection, the endpoint selected, nil on success.
    38  	// Returns nil, "", error on failure
    39  	NewConnection() (*grpc.ClientConn, string, error)
    40  	// UpdateEndpoints updates the endpoints of the ConnectionProducer
    41  	// to be the given endpoints
    42  	UpdateEndpoints(endpoints []string)
    43  }
    44  
    45  type connProducer struct {
    46  	sync.RWMutex
    47  	endpoints []string
    48  	connect   ConnectionFactory
    49  }
    50  
    51  // NewConnectionProducer creates a new ConnectionProducer with given endpoints and connection factory.
    52  // It returns nil, if the given endpoints slice is empty.
    53  func NewConnectionProducer(factory ConnectionFactory, endpoints []string) ConnectionProducer {
    54  	if len(endpoints) == 0 {
    55  		return nil
    56  	}
    57  	return &connProducer{endpoints: endpoints, connect: factory}
    58  }
    59  
    60  // NewConnection creates a new connection.
    61  // Returns the connection, the endpoint selected, nil on success.
    62  // Returns nil, "", error on failure
    63  func (cp *connProducer) NewConnection() (*grpc.ClientConn, string, error) {
    64  	cp.RLock()
    65  	defer cp.RUnlock()
    66  
    67  	endpoints := shuffle(cp.endpoints)
    68  	for _, endpoint := range endpoints {
    69  		conn, err := cp.connect(endpoint)
    70  		if err != nil {
    71  			logger.Error("Failed connecting to", endpoint, ", error:", err)
    72  			continue
    73  		}
    74  		return conn, endpoint, nil
    75  	}
    76  	return nil, "", fmt.Errorf("Could not connect to any of the endpoints: %v", endpoints)
    77  }
    78  
    79  // UpdateEndpoints updates the endpoints of the ConnectionProducer
    80  // to be the given endpoints
    81  func (cp *connProducer) UpdateEndpoints(endpoints []string) {
    82  	if len(endpoints) == 0 {
    83  		// Ignore updates with empty endpoints
    84  		return
    85  	}
    86  	cp.Lock()
    87  	defer cp.Unlock()
    88  	cp.endpoints = endpoints
    89  }
    90  
    91  func shuffle(a []string) []string {
    92  	n := len(a)
    93  	returnedSlice := make([]string, n)
    94  	indices := rand.Perm(n)
    95  	for i, idx := range indices {
    96  		returnedSlice[i] = a[idx]
    97  	}
    98  	return returnedSlice
    99  }
   100  
   101  // filterOut receives a slice of strings and a string to filter out
   102  // and returns the slice without the string
   103  func filterOut(a []string, filteredOut string) []string {
   104  	var slice2Return []string
   105  	for _, s := range a {
   106  		if s == filteredOut {
   107  			continue
   108  		}
   109  		slice2Return = append(slice2Return, s)
   110  	}
   111  	return slice2Return
   112  }