github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/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  		go onErr(errSendOverflow)
   264  		return
   265  	}
   266  
   267  	m := &msgSending{
   268  		envelope: msg.Envelope,
   269  		onErr:    onErr,
   270  	}
   271  
   272  	conn.outBuff <- m
   273  }
   274  
   275  func (conn *connection) serviceConnection() error {
   276  	errChan := make(chan error, 1)
   277  	msgChan := make(chan *proto.SignedGossipMessage, util.GetIntOrDefault("peer.gossip.recvBuffSize", defRecvBuffSize))
   278  	defer close(msgChan)
   279  
   280  	// Call stream.Recv() asynchronously in readFromStream(),
   281  	// and wait for either the Recv() call to end,
   282  	// or a signal to close the connection, which exits
   283  	// the method and makes the Recv() call to fail in the
   284  	// readFromStream() method
   285  	go conn.readFromStream(errChan, msgChan)
   286  
   287  	go conn.writeToStream()
   288  
   289  	for !conn.toDie() {
   290  		select {
   291  		case stop := <-conn.stopChan:
   292  			conn.logger.Debug("Closing reading from stream")
   293  			conn.stopChan <- stop
   294  			return nil
   295  		case err := <-errChan:
   296  			return err
   297  		case msg := <-msgChan:
   298  			conn.handler(msg)
   299  		}
   300  	}
   301  	return nil
   302  }
   303  
   304  func (conn *connection) writeToStream() {
   305  	for !conn.toDie() {
   306  		stream := conn.getStream()
   307  		if stream == nil {
   308  			conn.logger.Error(conn.pkiID, "Stream is nil, aborting!")
   309  			return
   310  		}
   311  		select {
   312  		case m := <-conn.outBuff:
   313  			err := stream.Send(m.envelope)
   314  			if err != nil {
   315  				go m.onErr(err)
   316  				return
   317  			}
   318  		case stop := <-conn.stopChan:
   319  			conn.logger.Debug("Closing writing to stream")
   320  			conn.stopChan <- stop
   321  			return
   322  		}
   323  	}
   324  }
   325  
   326  func (conn *connection) readFromStream(errChan chan error, msgChan chan *proto.SignedGossipMessage) {
   327  	defer func() {
   328  		recover()
   329  	}() // msgChan might be closed
   330  	for !conn.toDie() {
   331  		stream := conn.getStream()
   332  		if stream == nil {
   333  			conn.logger.Error(conn.pkiID, "Stream is nil, aborting!")
   334  			errChan <- errors.New("Stream is nil")
   335  			return
   336  		}
   337  		envelope, err := stream.Recv()
   338  		if conn.toDie() {
   339  			conn.logger.Debug(conn.pkiID, "canceling read because closing")
   340  			return
   341  		}
   342  		if err != nil {
   343  			errChan <- err
   344  			conn.logger.Debug(conn.pkiID, "Got error, aborting:", err)
   345  			return
   346  		}
   347  		msg, err := envelope.ToGossipMessage()
   348  		if err != nil {
   349  			errChan <- err
   350  			conn.logger.Warning(conn.pkiID, "Got error, aborting:", err)
   351  		}
   352  		msgChan <- msg
   353  	}
   354  }
   355  
   356  func (conn *connection) getStream() stream {
   357  	conn.Lock()
   358  	defer conn.Unlock()
   359  
   360  	if conn.clientStream != nil && conn.serverStream != nil {
   361  		e := "Both client and server stream are not nil, something went wrong"
   362  		conn.logger.Error(e)
   363  	}
   364  
   365  	if conn.clientStream != nil {
   366  		return conn.clientStream
   367  	}
   368  
   369  	if conn.serverStream != nil {
   370  		return conn.serverStream
   371  	}
   372  
   373  	return nil
   374  }
   375  
   376  type msgSending struct {
   377  	envelope *proto.Envelope
   378  	onErr    func(error)
   379  }