github.com/evdatsion/aphelion-dpos-bft@v0.32.1/abci/client/grpc_client.go (about) 1 package abcicli 2 3 import ( 4 "fmt" 5 "net" 6 "sync" 7 "time" 8 9 context "golang.org/x/net/context" 10 grpc "google.golang.org/grpc" 11 12 "github.com/evdatsion/aphelion-dpos-bft/abci/types" 13 cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common" 14 ) 15 16 var _ Client = (*grpcClient)(nil) 17 18 // A stripped copy of the remoteClient that makes 19 // synchronous calls using grpc 20 type grpcClient struct { 21 cmn.BaseService 22 mustConnect bool 23 24 client types.ABCIApplicationClient 25 conn *grpc.ClientConn 26 27 mtx sync.Mutex 28 addr string 29 err error 30 resCb func(*types.Request, *types.Response) // listens to all callbacks 31 } 32 33 func NewGRPCClient(addr string, mustConnect bool) *grpcClient { 34 cli := &grpcClient{ 35 addr: addr, 36 mustConnect: mustConnect, 37 } 38 cli.BaseService = *cmn.NewBaseService(nil, "grpcClient", cli) 39 return cli 40 } 41 42 func dialerFunc(addr string, timeout time.Duration) (net.Conn, error) { 43 return cmn.Connect(addr) 44 } 45 46 func (cli *grpcClient) OnStart() error { 47 if err := cli.BaseService.OnStart(); err != nil { 48 return err 49 } 50 RETRY_LOOP: 51 for { 52 conn, err := grpc.Dial(cli.addr, grpc.WithInsecure(), grpc.WithDialer(dialerFunc)) 53 if err != nil { 54 if cli.mustConnect { 55 return err 56 } 57 cli.Logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v. Retrying...\n", cli.addr), "err", err) 58 time.Sleep(time.Second * dialRetryIntervalSeconds) 59 continue RETRY_LOOP 60 } 61 62 cli.Logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr) 63 client := types.NewABCIApplicationClient(conn) 64 cli.conn = conn 65 66 ENSURE_CONNECTED: 67 for { 68 _, err := client.Echo(context.Background(), &types.RequestEcho{Message: "hello"}, grpc.FailFast(true)) 69 if err == nil { 70 break ENSURE_CONNECTED 71 } 72 cli.Logger.Error("Echo failed", "err", err) 73 time.Sleep(time.Second * echoRetryIntervalSeconds) 74 } 75 76 cli.client = client 77 return nil 78 } 79 } 80 81 func (cli *grpcClient) OnStop() { 82 cli.BaseService.OnStop() 83 84 if cli.conn != nil { 85 cli.conn.Close() 86 } 87 } 88 89 func (cli *grpcClient) StopForError(err error) { 90 cli.mtx.Lock() 91 if !cli.IsRunning() { 92 return 93 } 94 95 if cli.err == nil { 96 cli.err = err 97 } 98 cli.mtx.Unlock() 99 100 cli.Logger.Error(fmt.Sprintf("Stopping abci.grpcClient for error: %v", err.Error())) 101 cli.Stop() 102 } 103 104 func (cli *grpcClient) Error() error { 105 cli.mtx.Lock() 106 defer cli.mtx.Unlock() 107 return cli.err 108 } 109 110 // Set listener for all responses 111 // NOTE: callback may get internally generated flush responses. 112 func (cli *grpcClient) SetResponseCallback(resCb Callback) { 113 cli.mtx.Lock() 114 cli.resCb = resCb 115 cli.mtx.Unlock() 116 } 117 118 //---------------------------------------- 119 // GRPC calls are synchronous, but some callbacks expect to be called asynchronously 120 // (eg. the mempool expects to be able to lock to remove bad txs from cache). 121 // To accommodate, we finish each call in its own go-routine, 122 // which is expensive, but easy - if you want something better, use the socket protocol! 123 // maybe one day, if people really want it, we use grpc streams, 124 // but hopefully not :D 125 126 func (cli *grpcClient) EchoAsync(msg string) *ReqRes { 127 req := types.ToRequestEcho(msg) 128 res, err := cli.client.Echo(context.Background(), req.GetEcho(), grpc.FailFast(true)) 129 if err != nil { 130 cli.StopForError(err) 131 } 132 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Echo{Echo: res}}) 133 } 134 135 func (cli *grpcClient) FlushAsync() *ReqRes { 136 req := types.ToRequestFlush() 137 res, err := cli.client.Flush(context.Background(), req.GetFlush(), grpc.FailFast(true)) 138 if err != nil { 139 cli.StopForError(err) 140 } 141 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Flush{Flush: res}}) 142 } 143 144 func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes { 145 req := types.ToRequestInfo(params) 146 res, err := cli.client.Info(context.Background(), req.GetInfo(), grpc.FailFast(true)) 147 if err != nil { 148 cli.StopForError(err) 149 } 150 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Info{Info: res}}) 151 } 152 153 func (cli *grpcClient) SetOptionAsync(params types.RequestSetOption) *ReqRes { 154 req := types.ToRequestSetOption(params) 155 res, err := cli.client.SetOption(context.Background(), req.GetSetOption(), grpc.FailFast(true)) 156 if err != nil { 157 cli.StopForError(err) 158 } 159 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_SetOption{SetOption: res}}) 160 } 161 162 func (cli *grpcClient) DeliverTxAsync(params types.RequestDeliverTx) *ReqRes { 163 req := types.ToRequestDeliverTx(params) 164 res, err := cli.client.DeliverTx(context.Background(), req.GetDeliverTx(), grpc.FailFast(true)) 165 if err != nil { 166 cli.StopForError(err) 167 } 168 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_DeliverTx{DeliverTx: res}}) 169 } 170 171 func (cli *grpcClient) CheckTxAsync(params types.RequestCheckTx) *ReqRes { 172 req := types.ToRequestCheckTx(params) 173 res, err := cli.client.CheckTx(context.Background(), req.GetCheckTx(), grpc.FailFast(true)) 174 if err != nil { 175 cli.StopForError(err) 176 } 177 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_CheckTx{CheckTx: res}}) 178 } 179 180 func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes { 181 req := types.ToRequestQuery(params) 182 res, err := cli.client.Query(context.Background(), req.GetQuery(), grpc.FailFast(true)) 183 if err != nil { 184 cli.StopForError(err) 185 } 186 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Query{Query: res}}) 187 } 188 189 func (cli *grpcClient) CommitAsync() *ReqRes { 190 req := types.ToRequestCommit() 191 res, err := cli.client.Commit(context.Background(), req.GetCommit(), grpc.FailFast(true)) 192 if err != nil { 193 cli.StopForError(err) 194 } 195 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Commit{Commit: res}}) 196 } 197 198 func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes { 199 req := types.ToRequestInitChain(params) 200 res, err := cli.client.InitChain(context.Background(), req.GetInitChain(), grpc.FailFast(true)) 201 if err != nil { 202 cli.StopForError(err) 203 } 204 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_InitChain{InitChain: res}}) 205 } 206 207 func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes { 208 req := types.ToRequestBeginBlock(params) 209 res, err := cli.client.BeginBlock(context.Background(), req.GetBeginBlock(), grpc.FailFast(true)) 210 if err != nil { 211 cli.StopForError(err) 212 } 213 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_BeginBlock{BeginBlock: res}}) 214 } 215 216 func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock) *ReqRes { 217 req := types.ToRequestEndBlock(params) 218 res, err := cli.client.EndBlock(context.Background(), req.GetEndBlock(), grpc.FailFast(true)) 219 if err != nil { 220 cli.StopForError(err) 221 } 222 return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_EndBlock{EndBlock: res}}) 223 } 224 225 func (cli *grpcClient) finishAsyncCall(req *types.Request, res *types.Response) *ReqRes { 226 reqres := NewReqRes(req) 227 reqres.Response = res // Set response 228 reqres.Done() // Release waiters 229 reqres.SetDone() // so reqRes.SetCallback will run the callback 230 231 // go routine for callbacks 232 go func() { 233 // Notify reqRes listener if set 234 if cb := reqres.GetCallback(); cb != nil { 235 cb(res) 236 } 237 238 // Notify client listener if set 239 if cli.resCb != nil { 240 cli.resCb(reqres.Request, res) 241 } 242 }() 243 return reqres 244 } 245 246 //---------------------------------------- 247 248 func (cli *grpcClient) FlushSync() error { 249 return nil 250 } 251 252 func (cli *grpcClient) EchoSync(msg string) (*types.ResponseEcho, error) { 253 reqres := cli.EchoAsync(msg) 254 // StopForError should already have been called if error is set 255 return reqres.Response.GetEcho(), cli.Error() 256 } 257 258 func (cli *grpcClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) { 259 reqres := cli.InfoAsync(req) 260 return reqres.Response.GetInfo(), cli.Error() 261 } 262 263 func (cli *grpcClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) { 264 reqres := cli.SetOptionAsync(req) 265 return reqres.Response.GetSetOption(), cli.Error() 266 } 267 268 func (cli *grpcClient) DeliverTxSync(params types.RequestDeliverTx) (*types.ResponseDeliverTx, error) { 269 reqres := cli.DeliverTxAsync(params) 270 return reqres.Response.GetDeliverTx(), cli.Error() 271 } 272 273 func (cli *grpcClient) CheckTxSync(params types.RequestCheckTx) (*types.ResponseCheckTx, error) { 274 reqres := cli.CheckTxAsync(params) 275 return reqres.Response.GetCheckTx(), cli.Error() 276 } 277 278 func (cli *grpcClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) { 279 reqres := cli.QueryAsync(req) 280 return reqres.Response.GetQuery(), cli.Error() 281 } 282 283 func (cli *grpcClient) CommitSync() (*types.ResponseCommit, error) { 284 reqres := cli.CommitAsync() 285 return reqres.Response.GetCommit(), cli.Error() 286 } 287 288 func (cli *grpcClient) InitChainSync(params types.RequestInitChain) (*types.ResponseInitChain, error) { 289 reqres := cli.InitChainAsync(params) 290 return reqres.Response.GetInitChain(), cli.Error() 291 } 292 293 func (cli *grpcClient) BeginBlockSync(params types.RequestBeginBlock) (*types.ResponseBeginBlock, error) { 294 reqres := cli.BeginBlockAsync(params) 295 return reqres.Response.GetBeginBlock(), cli.Error() 296 } 297 298 func (cli *grpcClient) EndBlockSync(params types.RequestEndBlock) (*types.ResponseEndBlock, error) { 299 reqres := cli.EndBlockAsync(params) 300 return reqres.Response.GetEndBlock(), cli.Error() 301 }