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 }