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  }