github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/core/deliverservice/deliveryclient.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package deliverclient
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"math"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/hyperledger/fabric/common/flogging"
    17  	"github.com/hyperledger/fabric/core/comm"
    18  	"github.com/hyperledger/fabric/core/deliverservice/blocksprovider"
    19  	"github.com/hyperledger/fabric/gossip/api"
    20  	"github.com/hyperledger/fabric/protos/orderer"
    21  	"github.com/op/go-logging"
    22  	"google.golang.org/grpc"
    23  )
    24  
    25  var logger *logging.Logger // package-level logger
    26  
    27  func init() {
    28  	logger = flogging.MustGetLogger("deliveryClient")
    29  }
    30  
    31  var (
    32  	reConnectTotalTimeThreshold = time.Second * 60 * 5
    33  	connTimeout                 = time.Second * 3
    34  	reConnectBackoffThreshold   = float64(time.Hour)
    35  )
    36  
    37  // DeliverService used to communicate with orderers to obtain
    38  // new blocks and send them to the committer service
    39  type DeliverService interface {
    40  	// StartDeliverForChannel dynamically starts delivery of new blocks from ordering service
    41  	// to channel peers.
    42  	StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo) error
    43  
    44  	// StopDeliverForChannel dynamically stops delivery of new blocks from ordering service
    45  	// to channel peers.
    46  	StopDeliverForChannel(chainID string) error
    47  
    48  	// Stop terminates delivery service and closes the connection
    49  	Stop()
    50  }
    51  
    52  // deliverServiceImpl the implementation of the delivery service
    53  // maintains connection to the ordering service and maps of
    54  // blocks providers
    55  type deliverServiceImpl struct {
    56  	conf           *Config
    57  	blockProviders map[string]blocksprovider.BlocksProvider
    58  	lock           sync.RWMutex
    59  	stopping       bool
    60  }
    61  
    62  // Config dictates the DeliveryService's properties,
    63  // namely how it connects to an ordering service endpoint,
    64  // how it verifies messages received from it,
    65  // and how it disseminates the messages to other peers
    66  type Config struct {
    67  	// ConnFactory returns a function that creates a connection to an endpoint
    68  	ConnFactory func(channelID string) func(endpoint string) (*grpc.ClientConn, error)
    69  	// ABCFactory creates an AtomicBroadcastClient out of a connection
    70  	ABCFactory func(*grpc.ClientConn) orderer.AtomicBroadcastClient
    71  	// CryptoSvc performs cryptographic actions like message verification and signing
    72  	// and identity validation
    73  	CryptoSvc api.MessageCryptoService
    74  	// Gossip enables to enumerate peers in the channel, send a message to peers,
    75  	// and add a block to the gossip state transfer layer
    76  	Gossip blocksprovider.GossipServiceAdapter
    77  	// Endpoints specifies the endpoints of the ordering service
    78  	Endpoints []string
    79  }
    80  
    81  // NewDeliverService construction function to create and initialize
    82  // delivery service instance. It tries to establish connection to
    83  // the specified in the configuration ordering service, in case it
    84  // fails to dial to it, return nil
    85  func NewDeliverService(conf *Config) (DeliverService, error) {
    86  	ds := &deliverServiceImpl{
    87  		conf:           conf,
    88  		blockProviders: make(map[string]blocksprovider.BlocksProvider),
    89  	}
    90  	if err := ds.validateConfiguration(); err != nil {
    91  		return nil, err
    92  	}
    93  	return ds, nil
    94  }
    95  
    96  func (d *deliverServiceImpl) validateConfiguration() error {
    97  	conf := d.conf
    98  	if len(conf.Endpoints) == 0 {
    99  		return errors.New("No endpoints specified")
   100  	}
   101  	if conf.Gossip == nil {
   102  		return errors.New("No gossip provider specified")
   103  	}
   104  	if conf.ABCFactory == nil {
   105  		return errors.New("No AtomicBroadcast factory specified")
   106  	}
   107  	if conf.ConnFactory == nil {
   108  		return errors.New("No connection factory specified")
   109  	}
   110  	if conf.CryptoSvc == nil {
   111  		return errors.New("No crypto service specified")
   112  	}
   113  	return nil
   114  }
   115  
   116  // StartDeliverForChannel starts blocks delivery for channel
   117  // initializes the grpc stream for given chainID, creates blocks provider instance
   118  // that spawns in go routine to read new blocks starting from the position provided by ledger
   119  // info instance.
   120  func (d *deliverServiceImpl) StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo) error {
   121  	d.lock.Lock()
   122  	defer d.lock.Unlock()
   123  	if d.stopping {
   124  		errMsg := fmt.Sprintf("Delivery service is stopping cannot join a new channel %s", chainID)
   125  		logger.Errorf(errMsg)
   126  		return errors.New(errMsg)
   127  	}
   128  	if _, exist := d.blockProviders[chainID]; exist {
   129  		errMsg := fmt.Sprintf("Delivery service - block provider already exists for %s found, can't start delivery", chainID)
   130  		logger.Errorf(errMsg)
   131  		return errors.New(errMsg)
   132  	} else {
   133  		client := d.newClient(chainID, ledgerInfo)
   134  		logger.Debug("This peer will pass blocks from orderer service to other peers for channel", chainID)
   135  		d.blockProviders[chainID] = blocksprovider.NewBlocksProvider(chainID, client, d.conf.Gossip, d.conf.CryptoSvc)
   136  		go d.blockProviders[chainID].DeliverBlocks()
   137  	}
   138  	return nil
   139  }
   140  
   141  // StopDeliverForChannel stops blocks delivery for channel by stopping channel block provider
   142  func (d *deliverServiceImpl) StopDeliverForChannel(chainID string) error {
   143  	d.lock.Lock()
   144  	defer d.lock.Unlock()
   145  	if d.stopping {
   146  		errMsg := fmt.Sprintf("Delivery service is stopping, cannot stop delivery for channel %s", chainID)
   147  		logger.Errorf(errMsg)
   148  		return errors.New(errMsg)
   149  	}
   150  	if client, exist := d.blockProviders[chainID]; exist {
   151  		client.Stop()
   152  		delete(d.blockProviders, chainID)
   153  		logger.Debug("This peer will stop pass blocks from orderer service to other peers")
   154  	} else {
   155  		errMsg := fmt.Sprintf("Delivery service - no block provider for %s found, can't stop delivery", chainID)
   156  		logger.Errorf(errMsg)
   157  		return errors.New(errMsg)
   158  	}
   159  	return nil
   160  }
   161  
   162  // Stop all service and release resources
   163  func (d *deliverServiceImpl) Stop() {
   164  	d.lock.Lock()
   165  	defer d.lock.Unlock()
   166  	// Marking flag to indicate the shutdown of the delivery service
   167  	d.stopping = true
   168  
   169  	for _, client := range d.blockProviders {
   170  		client.Stop()
   171  	}
   172  }
   173  
   174  func (d *deliverServiceImpl) newClient(chainID string, ledgerInfoProvider blocksprovider.LedgerInfo) *broadcastClient {
   175  	requester := &blocksRequester{
   176  		chainID: chainID,
   177  	}
   178  	broadcastSetup := func(bd blocksprovider.BlocksDeliverer) error {
   179  		return requester.RequestBlocks(ledgerInfoProvider)
   180  	}
   181  	backoffPolicy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   182  		if elapsedTime.Nanoseconds() > reConnectTotalTimeThreshold.Nanoseconds() {
   183  			return 0, false
   184  		}
   185  		sleepIncrement := float64(time.Millisecond * 500)
   186  		attempt := float64(attemptNum)
   187  		return time.Duration(math.Min(math.Pow(2, attempt)*sleepIncrement, reConnectBackoffThreshold)), true
   188  	}
   189  	connProd := comm.NewConnectionProducer(d.conf.ConnFactory(chainID), d.conf.Endpoints)
   190  	bClient := NewBroadcastClient(connProd, d.conf.ABCFactory, broadcastSetup, backoffPolicy)
   191  	requester.client = bClient
   192  	return bClient
   193  }
   194  
   195  func DefaultConnectionFactory(channelID string) func(endpoint string) (*grpc.ClientConn, error) {
   196  	return func(endpoint string) (*grpc.ClientConn, error) {
   197  		dialOpts := []grpc.DialOption{grpc.WithTimeout(connTimeout), grpc.WithBlock()}
   198  		// set max send/recv msg sizes
   199  		dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(comm.MaxRecvMsgSize()),
   200  			grpc.MaxCallSendMsgSize(comm.MaxSendMsgSize())))
   201  		// set the keepalive options
   202  		dialOpts = append(dialOpts, comm.ClientKeepaliveOptions()...)
   203  
   204  		if comm.TLSEnabled() {
   205  			creds, err := comm.GetCASupport().GetDeliverServiceCredentials(channelID)
   206  			if err != nil {
   207  				return nil, fmt.Errorf("Failed obtaining credentials for channel %s: %v", channelID, err)
   208  			}
   209  			dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds))
   210  		} else {
   211  			dialOpts = append(dialOpts, grpc.WithInsecure())
   212  		}
   213  		grpc.EnableTracing = true
   214  		return grpc.Dial(endpoint, dialOpts...)
   215  	}
   216  }
   217  
   218  func DefaultABCFactory(conn *grpc.ClientConn) orderer.AtomicBroadcastClient {
   219  	return orderer.NewAtomicBroadcastClient(conn)
   220  }