github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/core/deliverservice/client.go (about) 1 /* 2 Copyright IBM Corp. 2017 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 "sync" 23 "sync/atomic" 24 "time" 25 26 "github.com/hyperledger/fabric/core/comm" 27 "github.com/hyperledger/fabric/core/deliverservice/blocksprovider" 28 "github.com/hyperledger/fabric/protos/common" 29 "github.com/hyperledger/fabric/protos/orderer" 30 "golang.org/x/net/context" 31 "google.golang.org/grpc" 32 ) 33 34 // broadcastSetup is a function that is called by the broadcastClient immediately after each 35 // successful connection to the ordering service 36 type broadcastSetup func(blocksprovider.BlocksDeliverer) error 37 38 // retryPolicy receives as parameters the number of times the attempt has failed 39 // and a duration that specifies the total elapsed time passed since the first attempt. 40 // If further attempts should be made, it returns: 41 // - a time duration after which the next attempt would be made, true 42 // Else, a zero duration, false 43 type retryPolicy func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) 44 45 // clientFactory creates a gRPC broadcast client out of a ClientConn 46 type clientFactory func(*grpc.ClientConn) orderer.AtomicBroadcastClient 47 48 type broadcastClient struct { 49 stopFlag int32 50 sync.Mutex 51 stopChan chan struct{} 52 createClient clientFactory 53 shouldRetry retryPolicy 54 onConnect broadcastSetup 55 prod comm.ConnectionProducer 56 blocksprovider.BlocksDeliverer 57 conn *connection 58 } 59 60 // NewBroadcastClient returns a broadcastClient with the given params 61 func NewBroadcastClient(prod comm.ConnectionProducer, clFactory clientFactory, onConnect broadcastSetup, bos retryPolicy) *broadcastClient { 62 return &broadcastClient{prod: prod, onConnect: onConnect, shouldRetry: bos, createClient: clFactory, stopChan: make(chan struct{}, 1)} 63 } 64 65 // Recv receives a message from the ordering service 66 func (bc *broadcastClient) Recv() (*orderer.DeliverResponse, error) { 67 o, err := bc.try(func() (interface{}, error) { 68 if bc.shouldStop() { 69 return nil, errors.New("closing") 70 } 71 return bc.BlocksDeliverer.Recv() 72 }) 73 if err != nil { 74 return nil, err 75 } 76 return o.(*orderer.DeliverResponse), nil 77 } 78 79 // Send sends a message to the ordering service 80 func (bc *broadcastClient) Send(msg *common.Envelope) error { 81 _, err := bc.try(func() (interface{}, error) { 82 if bc.shouldStop() { 83 return nil, errors.New("closing") 84 } 85 return nil, bc.BlocksDeliverer.Send(msg) 86 }) 87 return err 88 } 89 90 func (bc *broadcastClient) try(action func() (interface{}, error)) (interface{}, error) { 91 attempt := 0 92 start := time.Now() 93 var backoffDuration time.Duration 94 retry := true 95 for retry && !bc.shouldStop() { 96 attempt++ 97 resp, err := bc.doAction(action) 98 if err != nil { 99 backoffDuration, retry = bc.shouldRetry(attempt, time.Since(start)) 100 if !retry { 101 break 102 } 103 bc.sleep(backoffDuration) 104 continue 105 } 106 return resp, nil 107 } 108 if bc.shouldStop() { 109 return nil, errors.New("Client is closing") 110 } 111 return nil, fmt.Errorf("Attempts (%d) or elapsed time (%v) exhausted", attempt, time.Since(start)) 112 } 113 114 func (bc *broadcastClient) doAction(action func() (interface{}, error)) (interface{}, error) { 115 if bc.conn == nil { 116 err := bc.connect() 117 if err != nil { 118 return nil, err 119 } 120 } 121 resp, err := action() 122 if err != nil { 123 bc.Disconnect() 124 return nil, err 125 } 126 return resp, nil 127 } 128 129 func (bc *broadcastClient) sleep(duration time.Duration) { 130 select { 131 case <-time.After(duration): 132 case <-bc.stopChan: 133 } 134 } 135 136 func (bc *broadcastClient) connect() error { 137 conn, endpoint, err := bc.prod.NewConnection() 138 if err != nil { 139 logger.Error("Failed obtaining connection:", err) 140 return err 141 } 142 ctx, cf := context.WithCancel(context.Background()) 143 abc, err := bc.createClient(conn).Deliver(ctx) 144 if err != nil { 145 logger.Error("Connection to ", endpoint, "established but was unable to create gRPC stream:", err) 146 conn.Close() 147 return err 148 } 149 err = bc.afterConnect(conn, abc, cf) 150 if err == nil { 151 return nil 152 } 153 // If we reached here, lets make sure connection is closed 154 // and nullified before we return 155 bc.Disconnect() 156 return err 157 } 158 159 func (bc *broadcastClient) afterConnect(conn *grpc.ClientConn, abc orderer.AtomicBroadcast_DeliverClient, cf context.CancelFunc) error { 160 bc.Lock() 161 bc.conn = &connection{ClientConn: conn, cancel: cf} 162 bc.BlocksDeliverer = abc 163 if bc.shouldStop() { 164 bc.Unlock() 165 return errors.New("closing") 166 } 167 bc.Unlock() 168 // If the client is closed at this point- before onConnect, 169 // any use of this object by onConnect would return an error. 170 err := bc.onConnect(bc) 171 // If the client is closed right after onConnect, but before 172 // the following lock- this method would return an error because 173 // the client has been closed. 174 bc.Lock() 175 defer bc.Unlock() 176 if bc.shouldStop() { 177 return errors.New("closing") 178 } 179 // If the client is closed right after this method exits, 180 // it's because this method returned nil and not an error. 181 // So- connect() would return nil also, and the flow of the goroutine 182 // is returned to doAction(), where action() is invoked - and is configured 183 // to check whether the client has closed or not. 184 if err == nil { 185 return nil 186 } 187 logger.Error("Failed setting up broadcast:", err) 188 return err 189 } 190 191 func (bc *broadcastClient) shouldStop() bool { 192 return atomic.LoadInt32(&bc.stopFlag) == int32(1) 193 } 194 195 // Close makes the client close its connection and shut down 196 func (bc *broadcastClient) Close() { 197 bc.Lock() 198 defer bc.Unlock() 199 if bc.shouldStop() { 200 return 201 } 202 atomic.StoreInt32(&bc.stopFlag, int32(1)) 203 bc.stopChan <- struct{}{} 204 if bc.conn == nil { 205 return 206 } 207 bc.conn.Close() 208 } 209 210 // Disconnect makes the client close the existing connection 211 func (bc *broadcastClient) Disconnect() { 212 bc.Lock() 213 defer bc.Unlock() 214 if bc.conn == nil { 215 return 216 } 217 bc.conn.Close() 218 bc.conn = nil 219 bc.BlocksDeliverer = nil 220 } 221 222 type connection struct { 223 sync.Once 224 *grpc.ClientConn 225 cancel context.CancelFunc 226 } 227 228 func (c *connection) Close() error { 229 var err error 230 c.Once.Do(func() { 231 c.cancel() 232 err = c.ClientConn.Close() 233 }) 234 return err 235 }