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 }