github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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 abc, err := bc.createClient(conn).Deliver(context.Background()) 143 if err != nil { 144 logger.Error("Connection to ", endpoint, "established but was unable to create gRPC stream:", err) 145 conn.Close() 146 return err 147 } 148 err = bc.afterConnect(conn, abc) 149 if err == nil { 150 return nil 151 } 152 // If we reached here, lets make sure connection is closed 153 // and nullified before we return 154 bc.disconnect() 155 return err 156 } 157 158 func (bc *broadcastClient) afterConnect(conn *grpc.ClientConn, abc orderer.AtomicBroadcast_DeliverClient) error { 159 bc.Lock() 160 bc.conn = &connection{ClientConn: conn} 161 bc.BlocksDeliverer = abc 162 if bc.shouldStop() { 163 bc.Unlock() 164 return errors.New("closing") 165 } 166 bc.Unlock() 167 // If the client is closed at this point- before onConnect, 168 // any use of this object by onConnect would return an error. 169 err := bc.onConnect(bc) 170 // If the client is closed right after onConnect, but before 171 // the following lock- this method would return an error because 172 // the client has been closed. 173 bc.Lock() 174 defer bc.Unlock() 175 if bc.shouldStop() { 176 return errors.New("closing") 177 } 178 // If the client is closed right after this method exits, 179 // it's because this method returned nil and not an error. 180 // So- connect() would return nil also, and the flow of the goroutine 181 // is returned to doAction(), where action() is invoked - and is configured 182 // to check whether the client has closed or not. 183 if err == nil { 184 return nil 185 } 186 logger.Error("Failed setting up broadcast:", err) 187 return err 188 } 189 190 func (bc *broadcastClient) shouldStop() bool { 191 return atomic.LoadInt32(&bc.stopFlag) == int32(1) 192 } 193 194 func (bc *broadcastClient) Close() { 195 bc.Lock() 196 defer bc.Unlock() 197 if bc.shouldStop() { 198 return 199 } 200 atomic.StoreInt32(&bc.stopFlag, int32(1)) 201 bc.stopChan <- struct{}{} 202 if bc.conn == nil { 203 return 204 } 205 bc.conn.Close() 206 } 207 208 func (bc *broadcastClient) disconnect() { 209 bc.Lock() 210 defer bc.Unlock() 211 if bc.conn == nil { 212 return 213 } 214 bc.conn.Close() 215 bc.conn = nil 216 bc.BlocksDeliverer = nil 217 } 218 219 type connection struct { 220 *grpc.ClientConn 221 sync.Once 222 } 223 224 func (c *connection) Close() error { 225 var err error 226 c.Once.Do(func() { 227 err = c.ClientConn.Close() 228 }) 229 return err 230 }