github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/gossip/comm/conn.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package comm 8 9 import ( 10 "errors" 11 "sync" 12 "sync/atomic" 13 14 "github.com/hyperledger/fabric/gossip/common" 15 "github.com/hyperledger/fabric/gossip/util" 16 proto "github.com/hyperledger/fabric/protos/gossip" 17 "github.com/op/go-logging" 18 "golang.org/x/net/context" 19 "google.golang.org/grpc" 20 ) 21 22 type handler func(message *proto.SignedGossipMessage) 23 24 type connFactory interface { 25 createConnection(endpoint string, pkiID common.PKIidType) (*connection, error) 26 } 27 28 type connectionStore struct { 29 logger *logging.Logger // logger 30 isClosing bool // whether this connection store is shutting down 31 connFactory connFactory // creates a connection to remote peer 32 sync.RWMutex // synchronize access to shared variables 33 pki2Conn map[string]*connection // mapping between pkiID to connections 34 destinationLocks map[string]*sync.RWMutex //mapping between pkiIDs and locks, 35 // used to prevent concurrent connection establishment to the same remote endpoint 36 } 37 38 func newConnStore(connFactory connFactory, logger *logging.Logger) *connectionStore { 39 return &connectionStore{ 40 connFactory: connFactory, 41 isClosing: false, 42 pki2Conn: make(map[string]*connection), 43 destinationLocks: make(map[string]*sync.RWMutex), 44 logger: logger, 45 } 46 } 47 48 func (cs *connectionStore) getConnection(peer *RemotePeer) (*connection, error) { 49 cs.RLock() 50 isClosing := cs.isClosing 51 cs.RUnlock() 52 53 if isClosing { 54 return nil, errors.New("Shutting down") 55 } 56 57 pkiID := peer.PKIID 58 endpoint := peer.Endpoint 59 60 cs.Lock() 61 destinationLock, hasConnected := cs.destinationLocks[string(pkiID)] 62 if !hasConnected { 63 destinationLock = &sync.RWMutex{} 64 cs.destinationLocks[string(pkiID)] = destinationLock 65 } 66 cs.Unlock() 67 68 destinationLock.Lock() 69 70 cs.RLock() 71 conn, exists := cs.pki2Conn[string(pkiID)] 72 if exists { 73 cs.RUnlock() 74 destinationLock.Unlock() 75 return conn, nil 76 } 77 cs.RUnlock() 78 79 createdConnection, err := cs.connFactory.createConnection(endpoint, pkiID) 80 81 destinationLock.Unlock() 82 83 cs.RLock() 84 isClosing = cs.isClosing 85 cs.RUnlock() 86 if isClosing { 87 return nil, errors.New("ConnStore is closing") 88 } 89 90 cs.Lock() 91 delete(cs.destinationLocks, string(pkiID)) 92 defer cs.Unlock() 93 94 // check again, maybe someone connected to us during the connection creation? 95 conn, exists = cs.pki2Conn[string(pkiID)] 96 97 if exists { 98 if createdConnection != nil { 99 createdConnection.close() 100 } 101 return conn, nil 102 } 103 104 // no one connected to us AND we failed connecting! 105 if err != nil { 106 return nil, err 107 } 108 109 // at this point in the code, we created a connection to a remote peer 110 conn = createdConnection 111 cs.pki2Conn[string(createdConnection.pkiID)] = conn 112 113 go conn.serviceConnection() 114 115 return conn, nil 116 } 117 118 func (cs *connectionStore) connNum() int { 119 cs.RLock() 120 defer cs.RUnlock() 121 return len(cs.pki2Conn) 122 } 123 124 func (cs *connectionStore) closeConn(peer *RemotePeer) { 125 cs.Lock() 126 defer cs.Unlock() 127 128 if conn, exists := cs.pki2Conn[string(peer.PKIID)]; exists { 129 conn.close() 130 delete(cs.pki2Conn, string(conn.pkiID)) 131 } 132 } 133 134 func (cs *connectionStore) shutdown() { 135 cs.Lock() 136 cs.isClosing = true 137 pkiIds2conn := cs.pki2Conn 138 139 var connections2Close []*connection 140 for _, conn := range pkiIds2conn { 141 connections2Close = append(connections2Close, conn) 142 } 143 cs.Unlock() 144 145 wg := sync.WaitGroup{} 146 for _, conn := range connections2Close { 147 wg.Add(1) 148 go func(conn *connection) { 149 cs.closeByPKIid(conn.pkiID) 150 wg.Done() 151 }(conn) 152 } 153 wg.Wait() 154 } 155 156 func (cs *connectionStore) onConnected(serverStream proto.Gossip_GossipStreamServer, connInfo *proto.ConnectionInfo) *connection { 157 cs.Lock() 158 defer cs.Unlock() 159 160 if c, exists := cs.pki2Conn[string(connInfo.Identity)]; exists { 161 c.close() 162 } 163 164 return cs.registerConn(connInfo, serverStream) 165 } 166 167 func (cs *connectionStore) registerConn(connInfo *proto.ConnectionInfo, serverStream proto.Gossip_GossipStreamServer) *connection { 168 conn := newConnection(nil, nil, nil, serverStream) 169 conn.pkiID = connInfo.ID 170 conn.info = connInfo 171 conn.logger = cs.logger 172 cs.pki2Conn[string(connInfo.ID)] = conn 173 return conn 174 } 175 176 func (cs *connectionStore) closeByPKIid(pkiID common.PKIidType) { 177 cs.Lock() 178 defer cs.Unlock() 179 if conn, exists := cs.pki2Conn[string(pkiID)]; exists { 180 conn.close() 181 delete(cs.pki2Conn, string(pkiID)) 182 } 183 } 184 185 func newConnection(cl proto.GossipClient, c *grpc.ClientConn, cs proto.Gossip_GossipStreamClient, ss proto.Gossip_GossipStreamServer) *connection { 186 connection := &connection{ 187 outBuff: make(chan *msgSending, util.GetIntOrDefault("peer.gossip.sendBuffSize", defSendBuffSize)), 188 cl: cl, 189 conn: c, 190 clientStream: cs, 191 serverStream: ss, 192 stopFlag: int32(0), 193 stopChan: make(chan struct{}, 1), 194 } 195 196 return connection 197 } 198 199 type connection struct { 200 cancel context.CancelFunc 201 info *proto.ConnectionInfo 202 outBuff chan *msgSending 203 logger *logging.Logger // logger 204 pkiID common.PKIidType // pkiID of the remote endpoint 205 handler handler // function to invoke upon a message reception 206 conn *grpc.ClientConn // gRPC connection to remote endpoint 207 cl proto.GossipClient // gRPC stub of remote endpoint 208 clientStream proto.Gossip_GossipStreamClient // client-side stream to remote endpoint 209 serverStream proto.Gossip_GossipStreamServer // server-side stream to remote endpoint 210 stopFlag int32 // indicates whether this connection is in process of stopping 211 stopChan chan struct{} // a method to stop the server-side gRPC call from a different go-routine 212 sync.RWMutex // synchronizes access to shared variables 213 } 214 215 func (conn *connection) close() { 216 if conn.toDie() { 217 return 218 } 219 220 amIFirst := atomic.CompareAndSwapInt32(&conn.stopFlag, int32(0), int32(1)) 221 if !amIFirst { 222 return 223 } 224 225 conn.stopChan <- struct{}{} 226 227 conn.Lock() 228 229 if conn.clientStream != nil { 230 conn.clientStream.CloseSend() 231 } 232 if conn.conn != nil { 233 conn.conn.Close() 234 } 235 236 if conn.cancel != nil { 237 conn.cancel() 238 } 239 240 conn.Unlock() 241 242 } 243 244 func (conn *connection) toDie() bool { 245 return atomic.LoadInt32(&(conn.stopFlag)) == int32(1) 246 } 247 248 func (conn *connection) send(msg *proto.SignedGossipMessage, onErr func(error)) { 249 conn.Lock() 250 defer conn.Unlock() 251 252 if len(conn.outBuff) == util.GetIntOrDefault("peer.gossip.sendBuffSize", defSendBuffSize) { 253 if conn.logger.IsEnabledFor(logging.DEBUG) { 254 conn.logger.Debug("Buffer to", conn.info.Endpoint, "overflowed, dropping message", msg.String()) 255 } 256 return 257 } 258 259 m := &msgSending{ 260 envelope: msg.Envelope, 261 onErr: onErr, 262 } 263 264 conn.outBuff <- m 265 } 266 267 func (conn *connection) serviceConnection() error { 268 errChan := make(chan error, 1) 269 msgChan := make(chan *proto.SignedGossipMessage, util.GetIntOrDefault("peer.gossip.recvBuffSize", defRecvBuffSize)) 270 defer close(msgChan) 271 272 // Call stream.Recv() asynchronously in readFromStream(), 273 // and wait for either the Recv() call to end, 274 // or a signal to close the connection, which exits 275 // the method and makes the Recv() call to fail in the 276 // readFromStream() method 277 go conn.readFromStream(errChan, msgChan) 278 279 go conn.writeToStream() 280 281 for !conn.toDie() { 282 select { 283 case stop := <-conn.stopChan: 284 conn.logger.Debug("Closing reading from stream") 285 conn.stopChan <- stop 286 return nil 287 case err := <-errChan: 288 return err 289 case msg := <-msgChan: 290 conn.handler(msg) 291 } 292 } 293 return nil 294 } 295 296 func (conn *connection) writeToStream() { 297 for !conn.toDie() { 298 stream := conn.getStream() 299 if stream == nil { 300 conn.logger.Error(conn.pkiID, "Stream is nil, aborting!") 301 return 302 } 303 select { 304 case m := <-conn.outBuff: 305 err := stream.Send(m.envelope) 306 if err != nil { 307 go m.onErr(err) 308 return 309 } 310 case stop := <-conn.stopChan: 311 conn.logger.Debug("Closing writing to stream") 312 conn.stopChan <- stop 313 return 314 } 315 } 316 } 317 318 func (conn *connection) readFromStream(errChan chan error, msgChan chan *proto.SignedGossipMessage) { 319 defer func() { 320 recover() 321 }() // msgChan might be closed 322 for !conn.toDie() { 323 stream := conn.getStream() 324 if stream == nil { 325 conn.logger.Error(conn.pkiID, "Stream is nil, aborting!") 326 errChan <- errors.New("Stream is nil") 327 return 328 } 329 envelope, err := stream.Recv() 330 if conn.toDie() { 331 conn.logger.Debug(conn.pkiID, "canceling read because closing") 332 return 333 } 334 if err != nil { 335 errChan <- err 336 conn.logger.Debug(conn.pkiID, "Got error, aborting:", err) 337 return 338 } 339 msg, err := envelope.ToGossipMessage() 340 if err != nil { 341 errChan <- err 342 conn.logger.Warning(conn.pkiID, "Got error, aborting:", err) 343 } 344 msgChan <- msg 345 } 346 } 347 348 func (conn *connection) getStream() stream { 349 conn.Lock() 350 defer conn.Unlock() 351 352 if conn.clientStream != nil && conn.serverStream != nil { 353 e := "Both client and server stream are not nil, something went wrong" 354 conn.logger.Error(e) 355 } 356 357 if conn.clientStream != nil { 358 return conn.clientStream 359 } 360 361 if conn.serverStream != nil { 362 return conn.serverStream 363 } 364 365 return nil 366 } 367 368 type msgSending struct { 369 envelope *proto.Envelope 370 onErr func(error) 371 }