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 }