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  }