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