github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/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  	"time"
    24  
    25  	"github.com/hyperledger/fabric/common/flogging"
    26  	"google.golang.org/grpc"
    27  )
    28  
    29  var logger = flogging.MustGetLogger("ConnProducer")
    30  
    31  // ConnectionFactory creates a connection to a certain endpoint
    32  type ConnectionFactory func(endpoint string) (*grpc.ClientConn, error)
    33  
    34  // ConnectionProducer produces connections out of a set of predefined
    35  // endpoints
    36  type ConnectionProducer interface {
    37  	// NewConnection creates a new connection.
    38  	// Returns the connection, the endpoint selected, nil on success.
    39  	// Returns nil, "", error on failure
    40  	NewConnection() (*grpc.ClientConn, string, error)
    41  	// UpdateEndpoints updates the endpoints of the ConnectionProducer
    42  	// to be the given endpoints
    43  	UpdateEndpoints(endpoints []string)
    44  }
    45  
    46  type connProducer struct {
    47  	sync.RWMutex
    48  	endpoints []string
    49  	connect   ConnectionFactory
    50  }
    51  
    52  // NewConnectionProducer creates a new ConnectionProducer with given endpoints and connection factory.
    53  // It returns nil, if the given endpoints slice is empty.
    54  func NewConnectionProducer(factory ConnectionFactory, endpoints []string) ConnectionProducer {
    55  	if len(endpoints) == 0 {
    56  		return nil
    57  	}
    58  	return &connProducer{endpoints: endpoints, connect: factory}
    59  }
    60  
    61  // NewConnection creates a new connection.
    62  // Returns the connection, the endpoint selected, nil on success.
    63  // Returns nil, "", error on failure
    64  func (cp *connProducer) NewConnection() (*grpc.ClientConn, string, error) {
    65  	cp.RLock()
    66  	defer cp.RUnlock()
    67  
    68  	endpoints := shuffle(cp.endpoints)
    69  	for _, endpoint := range endpoints {
    70  		conn, err := cp.connect(endpoint)
    71  		if err != nil {
    72  			logger.Error("Failed connecting to", endpoint, ", error:", err)
    73  			continue
    74  		}
    75  		return conn, endpoint, nil
    76  	}
    77  	return nil, "", fmt.Errorf("Could not connect to any of the endpoints: %v", endpoints)
    78  }
    79  
    80  // UpdateEndpoints updates the endpoints of the ConnectionProducer
    81  // to be the given endpoints
    82  func (cp *connProducer) UpdateEndpoints(endpoints []string) {
    83  	if len(endpoints) == 0 {
    84  		// Ignore updates with empty endpoints
    85  		return
    86  	}
    87  	cp.Lock()
    88  	defer cp.Unlock()
    89  	cp.endpoints = endpoints
    90  }
    91  
    92  func shuffle(a []string) []string {
    93  	n := len(a)
    94  	returnedSlice := make([]string, n)
    95  	rand.Seed(time.Now().UnixNano())
    96  	indices := rand.Perm(n)
    97  	for i, idx := range indices {
    98  		returnedSlice[i] = a[idx]
    99  	}
   100  	return returnedSlice
   101  }