github.com/evdatsion/aphelion-dpos-bft@v0.32.1/abci/client/socket_client.go (about) 1 package abcicli 2 3 import ( 4 "bufio" 5 "container/list" 6 "errors" 7 "fmt" 8 "net" 9 "reflect" 10 "sync" 11 "time" 12 13 "github.com/evdatsion/aphelion-dpos-bft/abci/types" 14 cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common" 15 ) 16 17 const reqQueueSize = 256 // TODO make configurable 18 // const maxResponseSize = 1048576 // 1MB TODO make configurable 19 const flushThrottleMS = 20 // Don't wait longer than... 20 21 var _ Client = (*socketClient)(nil) 22 23 // This is goroutine-safe, but users should beware that 24 // the application in general is not meant to be interfaced 25 // with concurrent callers. 26 type socketClient struct { 27 cmn.BaseService 28 29 addr string 30 mustConnect bool 31 conn net.Conn 32 33 reqQueue chan *ReqRes 34 flushTimer *cmn.ThrottleTimer 35 36 mtx sync.Mutex 37 err error 38 reqSent *list.List // list of requests sent, waiting for response 39 resCb func(*types.Request, *types.Response) // called on all requests, if set. 40 41 } 42 43 func NewSocketClient(addr string, mustConnect bool) *socketClient { 44 cli := &socketClient{ 45 reqQueue: make(chan *ReqRes, reqQueueSize), 46 flushTimer: cmn.NewThrottleTimer("socketClient", flushThrottleMS), 47 mustConnect: mustConnect, 48 49 addr: addr, 50 reqSent: list.New(), 51 resCb: nil, 52 } 53 cli.BaseService = *cmn.NewBaseService(nil, "socketClient", cli) 54 return cli 55 } 56 57 func (cli *socketClient) OnStart() error { 58 var err error 59 var conn net.Conn 60 RETRY_LOOP: 61 for { 62 conn, err = cmn.Connect(cli.addr) 63 if err != nil { 64 if cli.mustConnect { 65 return err 66 } 67 cli.Logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v. Retrying...", cli.addr), "err", err) 68 time.Sleep(time.Second * dialRetryIntervalSeconds) 69 continue RETRY_LOOP 70 } 71 cli.conn = conn 72 73 go cli.sendRequestsRoutine(conn) 74 go cli.recvResponseRoutine(conn) 75 76 return nil 77 } 78 } 79 80 func (cli *socketClient) OnStop() { 81 if cli.conn != nil { 82 cli.conn.Close() 83 } 84 85 cli.mtx.Lock() 86 defer cli.mtx.Unlock() 87 cli.flushQueue() 88 } 89 90 // Stop the client and set the error 91 func (cli *socketClient) StopForError(err error) { 92 if !cli.IsRunning() { 93 return 94 } 95 96 cli.mtx.Lock() 97 if cli.err == nil { 98 cli.err = err 99 } 100 cli.mtx.Unlock() 101 102 cli.Logger.Error(fmt.Sprintf("Stopping abci.socketClient for error: %v", err.Error())) 103 cli.Stop() 104 } 105 106 func (cli *socketClient) Error() error { 107 cli.mtx.Lock() 108 defer cli.mtx.Unlock() 109 return cli.err 110 } 111 112 // Set listener for all responses 113 // NOTE: callback may get internally generated flush responses. 114 func (cli *socketClient) SetResponseCallback(resCb Callback) { 115 cli.mtx.Lock() 116 cli.resCb = resCb 117 cli.mtx.Unlock() 118 } 119 120 //---------------------------------------- 121 122 func (cli *socketClient) sendRequestsRoutine(conn net.Conn) { 123 124 w := bufio.NewWriter(conn) 125 for { 126 select { 127 case <-cli.flushTimer.Ch: 128 select { 129 case cli.reqQueue <- NewReqRes(types.ToRequestFlush()): 130 default: 131 // Probably will fill the buffer, or retry later. 132 } 133 case <-cli.Quit(): 134 return 135 case reqres := <-cli.reqQueue: 136 cli.willSendReq(reqres) 137 err := types.WriteMessage(reqres.Request, w) 138 if err != nil { 139 cli.StopForError(fmt.Errorf("Error writing msg: %v", err)) 140 return 141 } 142 // cli.Logger.Debug("Sent request", "requestType", reflect.TypeOf(reqres.Request), "request", reqres.Request) 143 if _, ok := reqres.Request.Value.(*types.Request_Flush); ok { 144 err = w.Flush() 145 if err != nil { 146 cli.StopForError(fmt.Errorf("Error flushing writer: %v", err)) 147 return 148 } 149 } 150 } 151 } 152 } 153 154 func (cli *socketClient) recvResponseRoutine(conn net.Conn) { 155 156 r := bufio.NewReader(conn) // Buffer reads 157 for { 158 var res = &types.Response{} 159 err := types.ReadMessage(r, res) 160 if err != nil { 161 cli.StopForError(err) 162 return 163 } 164 switch r := res.Value.(type) { 165 case *types.Response_Exception: 166 // XXX After setting cli.err, release waiters (e.g. reqres.Done()) 167 cli.StopForError(errors.New(r.Exception.Error)) 168 return 169 default: 170 // cli.Logger.Debug("Received response", "responseType", reflect.TypeOf(res), "response", res) 171 err := cli.didRecvResponse(res) 172 if err != nil { 173 cli.StopForError(err) 174 return 175 } 176 } 177 } 178 } 179 180 func (cli *socketClient) willSendReq(reqres *ReqRes) { 181 cli.mtx.Lock() 182 defer cli.mtx.Unlock() 183 cli.reqSent.PushBack(reqres) 184 } 185 186 func (cli *socketClient) didRecvResponse(res *types.Response) error { 187 cli.mtx.Lock() 188 defer cli.mtx.Unlock() 189 190 // Get the first ReqRes 191 next := cli.reqSent.Front() 192 if next == nil { 193 return fmt.Errorf("Unexpected result type %v when nothing expected", reflect.TypeOf(res.Value)) 194 } 195 reqres := next.Value.(*ReqRes) 196 if !resMatchesReq(reqres.Request, res) { 197 return fmt.Errorf("Unexpected result type %v when response to %v expected", 198 reflect.TypeOf(res.Value), reflect.TypeOf(reqres.Request.Value)) 199 } 200 201 reqres.Response = res // Set response 202 reqres.Done() // Release waiters 203 cli.reqSent.Remove(next) // Pop first item from linked list 204 205 // Notify client listener if set (global callback). 206 if cli.resCb != nil { 207 cli.resCb(reqres.Request, res) 208 } 209 210 // Notify reqRes listener if set (request specific callback). 211 // NOTE: it is possible this callback isn't set on the reqres object. 212 // at this point, in which case it will be called after, when it is set. 213 if cb := reqres.GetCallback(); cb != nil { 214 cb(res) 215 } 216 217 return nil 218 } 219 220 //---------------------------------------- 221 222 func (cli *socketClient) EchoAsync(msg string) *ReqRes { 223 return cli.queueRequest(types.ToRequestEcho(msg)) 224 } 225 226 func (cli *socketClient) FlushAsync() *ReqRes { 227 return cli.queueRequest(types.ToRequestFlush()) 228 } 229 230 func (cli *socketClient) InfoAsync(req types.RequestInfo) *ReqRes { 231 return cli.queueRequest(types.ToRequestInfo(req)) 232 } 233 234 func (cli *socketClient) SetOptionAsync(req types.RequestSetOption) *ReqRes { 235 return cli.queueRequest(types.ToRequestSetOption(req)) 236 } 237 238 func (cli *socketClient) DeliverTxAsync(req types.RequestDeliverTx) *ReqRes { 239 return cli.queueRequest(types.ToRequestDeliverTx(req)) 240 } 241 242 func (cli *socketClient) CheckTxAsync(req types.RequestCheckTx) *ReqRes { 243 return cli.queueRequest(types.ToRequestCheckTx(req)) 244 } 245 246 func (cli *socketClient) QueryAsync(req types.RequestQuery) *ReqRes { 247 return cli.queueRequest(types.ToRequestQuery(req)) 248 } 249 250 func (cli *socketClient) CommitAsync() *ReqRes { 251 return cli.queueRequest(types.ToRequestCommit()) 252 } 253 254 func (cli *socketClient) InitChainAsync(req types.RequestInitChain) *ReqRes { 255 return cli.queueRequest(types.ToRequestInitChain(req)) 256 } 257 258 func (cli *socketClient) BeginBlockAsync(req types.RequestBeginBlock) *ReqRes { 259 return cli.queueRequest(types.ToRequestBeginBlock(req)) 260 } 261 262 func (cli *socketClient) EndBlockAsync(req types.RequestEndBlock) *ReqRes { 263 return cli.queueRequest(types.ToRequestEndBlock(req)) 264 } 265 266 //---------------------------------------- 267 268 func (cli *socketClient) FlushSync() error { 269 reqRes := cli.queueRequest(types.ToRequestFlush()) 270 if err := cli.Error(); err != nil { 271 return err 272 } 273 reqRes.Wait() // NOTE: if we don't flush the queue, its possible to get stuck here 274 return cli.Error() 275 } 276 277 func (cli *socketClient) EchoSync(msg string) (*types.ResponseEcho, error) { 278 reqres := cli.queueRequest(types.ToRequestEcho(msg)) 279 cli.FlushSync() 280 return reqres.Response.GetEcho(), cli.Error() 281 } 282 283 func (cli *socketClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) { 284 reqres := cli.queueRequest(types.ToRequestInfo(req)) 285 cli.FlushSync() 286 return reqres.Response.GetInfo(), cli.Error() 287 } 288 289 func (cli *socketClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) { 290 reqres := cli.queueRequest(types.ToRequestSetOption(req)) 291 cli.FlushSync() 292 return reqres.Response.GetSetOption(), cli.Error() 293 } 294 295 func (cli *socketClient) DeliverTxSync(req types.RequestDeliverTx) (*types.ResponseDeliverTx, error) { 296 reqres := cli.queueRequest(types.ToRequestDeliverTx(req)) 297 cli.FlushSync() 298 return reqres.Response.GetDeliverTx(), cli.Error() 299 } 300 301 func (cli *socketClient) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCheckTx, error) { 302 reqres := cli.queueRequest(types.ToRequestCheckTx(req)) 303 cli.FlushSync() 304 return reqres.Response.GetCheckTx(), cli.Error() 305 } 306 307 func (cli *socketClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) { 308 reqres := cli.queueRequest(types.ToRequestQuery(req)) 309 cli.FlushSync() 310 return reqres.Response.GetQuery(), cli.Error() 311 } 312 313 func (cli *socketClient) CommitSync() (*types.ResponseCommit, error) { 314 reqres := cli.queueRequest(types.ToRequestCommit()) 315 cli.FlushSync() 316 return reqres.Response.GetCommit(), cli.Error() 317 } 318 319 func (cli *socketClient) InitChainSync(req types.RequestInitChain) (*types.ResponseInitChain, error) { 320 reqres := cli.queueRequest(types.ToRequestInitChain(req)) 321 cli.FlushSync() 322 return reqres.Response.GetInitChain(), cli.Error() 323 } 324 325 func (cli *socketClient) BeginBlockSync(req types.RequestBeginBlock) (*types.ResponseBeginBlock, error) { 326 reqres := cli.queueRequest(types.ToRequestBeginBlock(req)) 327 cli.FlushSync() 328 return reqres.Response.GetBeginBlock(), cli.Error() 329 } 330 331 func (cli *socketClient) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) { 332 reqres := cli.queueRequest(types.ToRequestEndBlock(req)) 333 cli.FlushSync() 334 return reqres.Response.GetEndBlock(), cli.Error() 335 } 336 337 //---------------------------------------- 338 339 func (cli *socketClient) queueRequest(req *types.Request) *ReqRes { 340 reqres := NewReqRes(req) 341 342 // TODO: set cli.err if reqQueue times out 343 cli.reqQueue <- reqres 344 345 // Maybe auto-flush, or unset auto-flush 346 switch req.Value.(type) { 347 case *types.Request_Flush: 348 cli.flushTimer.Unset() 349 default: 350 cli.flushTimer.Set() 351 } 352 353 return reqres 354 } 355 356 func (cli *socketClient) flushQueue() { 357 // mark all in-flight messages as resolved (they will get cli.Error()) 358 for req := cli.reqSent.Front(); req != nil; req = req.Next() { 359 reqres := req.Value.(*ReqRes) 360 reqres.Done() 361 } 362 363 // mark all queued messages as resolved 364 LOOP: 365 for { 366 select { 367 case reqres := <-cli.reqQueue: 368 reqres.Done() 369 default: 370 break LOOP 371 } 372 } 373 } 374 375 //---------------------------------------- 376 377 func resMatchesReq(req *types.Request, res *types.Response) (ok bool) { 378 switch req.Value.(type) { 379 case *types.Request_Echo: 380 _, ok = res.Value.(*types.Response_Echo) 381 case *types.Request_Flush: 382 _, ok = res.Value.(*types.Response_Flush) 383 case *types.Request_Info: 384 _, ok = res.Value.(*types.Response_Info) 385 case *types.Request_SetOption: 386 _, ok = res.Value.(*types.Response_SetOption) 387 case *types.Request_DeliverTx: 388 _, ok = res.Value.(*types.Response_DeliverTx) 389 case *types.Request_CheckTx: 390 _, ok = res.Value.(*types.Response_CheckTx) 391 case *types.Request_Commit: 392 _, ok = res.Value.(*types.Response_Commit) 393 case *types.Request_Query: 394 _, ok = res.Value.(*types.Response_Query) 395 case *types.Request_InitChain: 396 _, ok = res.Value.(*types.Response_InitChain) 397 case *types.Request_BeginBlock: 398 _, ok = res.Value.(*types.Response_BeginBlock) 399 case *types.Request_EndBlock: 400 _, ok = res.Value.(*types.Response_EndBlock) 401 } 402 return ok 403 }