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