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