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 }