Skip to content

Commit

Permalink
add timeout and middlewire (#78)
Browse files Browse the repository at this point in the history
* add request timeout for call rpc and batch call rpc
add middlewire for hook call rpc and batch call rpc

* fix typo error middlewire

* add description of how to use middleware and change log

* rename function log middleware to console middleware

Co-authored-by: dayong <[email protected]>
  • Loading branch information
wangdayong228 and dayong authored May 25, 2021
1 parent 9a3e8d2 commit f6846a2
Show file tree
Hide file tree
Showing 16 changed files with 419 additions and 319 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,29 @@ func main() {
}

```
### Use middleware to hook rpc request

Client applies method `UseCallRpcMiddleware` to set middleware for hooking `callRpc` method which is the core of all single rpc related methods. And `UseBatchCallRpcMiddleware` to set middleware for hooking `batchCallRPC`.

For example, use `CallRpcLogMiddleware` to log for rpc requests.
```golang
client.UseCallRpcMiddleware(middleware.CallRpcLogMiddleware)
```

Also you could
- customize middleware
- use multiple middleware

Notice that the middleware chain exectuion order is like onion, for example, use middleware A first and then middleware B
```go
client.UseCallRpcMiddleware(A)
client.UseCallRpcMiddleware(B)
```
the middleware execution order is
```
B --> A --> client.callRpc --> A --> B
```

## Appendix
### Mapping of solidity types to go types
This is a mapping table for map solidity types to go types when using contract methods GetData/Call/SendTransaction/DecodeEvent
Expand Down
34 changes: 29 additions & 5 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ request is reported through the Error field of the corresponding BatchElem.

Note that batch calls may not be executed atomically on the server side.

You could use UseBatchCallRpcMiddleware to add middleware for hooking
BatchCallRPC

#### func (*Client) BatchGetBlockConfirmationRisk

```go
Expand Down Expand Up @@ -286,6 +289,8 @@ result if no error occurred.
The result must be a pointer so that package json can unmarshal into it. You can
also pass nil, in which case the result is ignored.

You could use UseCallRpcMiddleware to add middleware for hooking CallRPC

#### func (*Client) CheckBalanceAgainstTransaction

```go
Expand Down Expand Up @@ -704,6 +709,24 @@ func (client *Client) SubscribeNewHeads(channel chan types.BlockHeader) (*rpc.Cl
SubscribeNewHeads subscribes all new block headers participating in the
consensus.

#### func (*Client) UseBatchCallRpcMiddleware

```go
func (client *Client) UseBatchCallRpcMiddleware(middleware middleware.BatchCallRpcMiddleware)
```
UseBatchCallRpcMiddleware set middleware to hook BatchCallRpc, for example use
middleware.BatchCallRpcLogMiddleware for logging batch request info. You can
customize your BatchCallRpcMiddleware and use multi BatchCallRpcMiddleware.

#### func (*Client) UseCallRpcMiddleware

```go
func (client *Client) UseCallRpcMiddleware(middleware middleware.CallRpcMiddleware)
```
UseCallRpcMiddleware set middleware to hook CallRpc, for example use
middleware.CallRpcLogMiddleware for logging request info. You can customize your
CallRpcMiddleware and use multi CallRpcMiddleware.

#### func (*Client) WaitForTransationBePacked

```go
Expand All @@ -722,11 +745,12 @@ WaitForTransationReceipt waits for transaction receipt valid

```go
type ClientOption struct {
KeystorePath string
RetryCount int
RetryInterval time.Duration
CallRpcLog func(method string, args []interface{}, result interface{}, resultError error, duration time.Duration)
BatchCallRPCLog func(b []rpc.BatchElem, err error, duration time.Duration)
KeystorePath string
// retry
RetryCount int
RetryInterval time.Duration
// timeout of request
RequestTimeout time.Duration
}
```

Expand Down
4 changes: 3 additions & 1 deletion changeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Go-conflux-sdk Change Log

## v1.0.9
- Apply middleware for hooking call rpc and batch call rpc
- Support set request rpc timeout in Client
## v1.0.0
Note: v1.0.0 is not impatable with v0.x, the changes are
- Change address format follow [CIP-37](https://github.com/Conflux-Chain/CIPs/blob/master/CIPs/cip-37.md)
Expand Down
86 changes: 59 additions & 27 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ package sdk

import (
"context"

"math/big"
"reflect"
"time"

"github.com/Conflux-Chain/go-conflux-sdk/constants"
"github.com/Conflux-Chain/go-conflux-sdk/middleware"
"github.com/Conflux-Chain/go-conflux-sdk/rpc"
"github.com/Conflux-Chain/go-conflux-sdk/types"
"github.com/Conflux-Chain/go-conflux-sdk/types/cfxaddress"
Expand All @@ -24,22 +26,25 @@ const errMsgApplyTxValues = "failed to apply default transaction values"

// Client represents a client to interact with Conflux blockchain.
type Client struct {
AccountManager AccountManagerOperator
nodeURL string
rpcRequester RpcRequester
networkID uint32
option ClientOption
AccountManager AccountManagerOperator
nodeURL string
rpcRequester RpcRequester
networkID uint32
option ClientOption
callRpcHandler middleware.CallRpcHandler
batchCallRpcHandler middleware.BatchCallRpcHandler
}

// ClientOption for set keystore path and flags for retry
//
// The simplest way to set logger is to use the types.DefaultCallRpcLog and types.DefaultBatchCallRPCLog
type ClientOption struct {
KeystorePath string
RetryCount int
RetryInterval time.Duration
CallRpcLog func(method string, args []interface{}, result interface{}, resultError error, duration time.Duration)
BatchCallRPCLog func(b []rpc.BatchElem, err error, duration time.Duration)
KeystorePath string
// retry
RetryCount int
RetryInterval time.Duration
// timeout of request
RequestTimeout time.Duration
}

// NewClient creates an instance of Client with specified conflux node url, it will creat account manager if option.KeystorePath not empty.
Expand Down Expand Up @@ -72,6 +77,8 @@ func newClientWithRetry(nodeURL string, clientOption ClientOption) (*Client, err
var client Client
client.nodeURL = nodeURL
client.option = clientOption
client.callRpcHandler = middleware.CallRpcHandlerFunc(client.callRpc)
client.batchCallRpcHandler = middleware.BatchCallRpcHandlerFunc(client.batchCallRPC)

rpcClient, err := rpc.Dial(nodeURL)
if err != nil {
Expand All @@ -93,15 +100,6 @@ func newClientWithRetry(nodeURL string, clientOption ClientOption) (*Client, err
}
}

if client.option.CallRpcLog == nil {
client.option.CallRpcLog = func(method string, args []interface{}, result interface{}, resultError error, duration time.Duration) {
}
}

if client.option.BatchCallRPCLog == nil {
client.option.BatchCallRPCLog = func(b []rpc.BatchElem, err error, duration time.Duration) {}
}

_, err = client.GetNetworkID()
if err != nil {
return nil, errors.Wrap(err, "failed to get networkID")
Expand Down Expand Up @@ -144,11 +142,24 @@ func (client *Client) MustNewAddress(base32OrHex string) types.Address {
//
// The result must be a pointer so that package json can unmarshal into it. You
// can also pass nil, in which case the result is ignored.
//
// You could use UseCallRpcMiddleware to add middleware for hooking CallRPC
func (client *Client) CallRPC(result interface{}, method string, args ...interface{}) error {
start := time.Now()
err := client.rpcRequester.Call(result, method, args...)
client.option.CallRpcLog(method, args, result, err, time.Since(start))
return err
return client.callRpcHandler.Handle(result, method, args...)
}

func (client *Client) callRpc(result interface{}, method string, args ...interface{}) error {
ctx, cancelFunc := client.genContext()
if cancelFunc != nil {
defer cancelFunc()
}
return client.rpcRequester.CallContext(ctx, result, method, args...)
}

// UseCallRpcMiddleware set middleware to hook CallRpc, for example use middleware.CallRpcLogMiddleware for logging request info.
// You can customize your CallRpcMiddleware and use multi CallRpcMiddleware.
func (client *Client) UseCallRpcMiddleware(middleware middleware.CallRpcMiddleware) {
client.callRpcHandler = middleware(client.callRpcHandler)
}

// BatchCallRPC sends all given requests as a single batch and waits for the server
Expand All @@ -158,11 +169,25 @@ func (client *Client) CallRPC(result interface{}, method string, args ...interfa
// a request is reported through the Error field of the corresponding BatchElem.
//
// Note that batch calls may not be executed atomically on the server side.
//
// You could use UseBatchCallRpcMiddleware to add middleware for hooking BatchCallRPC
func (client *Client) BatchCallRPC(b []rpc.BatchElem) error {
start := time.Now()
err := client.rpcRequester.BatchCall(b)
client.option.BatchCallRPCLog(b, err, time.Since(start))
return err
return client.batchCallRpcHandler.Handle(b)
}

func (client *Client) batchCallRPC(b []rpc.BatchElem) error {
ctx, cancelFunc := client.genContext()
if cancelFunc != nil {
defer cancelFunc()
}

return client.rpcRequester.BatchCallContext(ctx, b)
}

// UseBatchCallRpcMiddleware set middleware to hook BatchCallRpc, for example use middleware.BatchCallRpcLogMiddleware for logging batch request info.
// You can customize your BatchCallRpcMiddleware and use multi BatchCallRpcMiddleware.
func (client *Client) UseBatchCallRpcMiddleware(middleware middleware.BatchCallRpcMiddleware) {
client.batchCallRpcHandler = middleware(client.batchCallRpcHandler)
}

// SetAccountManager sets account manager for sign transaction
Expand Down Expand Up @@ -1089,3 +1114,10 @@ func get1stEpochIfy(epoch []*types.Epoch) *types.Epoch {
}
return realEpoch
}

func (client *Client) genContext() (context.Context, context.CancelFunc) {
if client.option.RequestTimeout > 0 {
return context.WithTimeout(context.Background(), client.option.RequestTimeout)
}
return context.Background(), nil
}
13 changes: 10 additions & 3 deletions client_retry.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ type rpcClientWithRetry struct {
}

func (r *rpcClientWithRetry) Call(resultPtr interface{}, method string, args ...interface{}) error {
return r.CallContext(context.Background(), resultPtr, method, args...)
}

func (r *rpcClientWithRetry) CallContext(ctx context.Context, resultPtr interface{}, method string, args ...interface{}) error {
remain := r.retryCount
for {

err := r.inner.Call(resultPtr, method, args...)
err := r.inner.CallContext(ctx, resultPtr, method, args...)
if err == nil {
return nil
}
Expand All @@ -42,7 +45,11 @@ func (r *rpcClientWithRetry) Call(resultPtr interface{}, method string, args ...
}

func (r *rpcClientWithRetry) BatchCall(b []rpc.BatchElem) error {
err := r.inner.BatchCall(b)
return r.BatchCallContext(context.Background(), b)
}

func (r *rpcClientWithRetry) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
err := r.inner.BatchCallContext(ctx, b)
if err == nil {
return nil
}
Expand All @@ -53,7 +60,7 @@ func (r *rpcClientWithRetry) BatchCall(b []rpc.BatchElem) error {

remain := r.retryCount
for {
if err = r.inner.BatchCall(b); err == nil {
if err = r.inner.BatchCallContext(ctx, b); err == nil {
return nil
}

Expand Down
10 changes: 5 additions & 5 deletions example/context/config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
NodeURL = "http://127.0.0.1:12537"
BlockHash = "0x589c98650b0531883edab079acceeae4eefd892a73871ef6e8f1b4f9a82dc1bf"
TransactionHash = "0xca3c7b7b43e0290bc7ba103f42cfabd78ced8504ec5b920276891a8da67ca6b1"
BlockHashOfNewContract = "0xf7a26c4455f320ea298226635c74e87e8f1de78d7d38ba2f8beb7b97bc826b94"
ERC20Address = "NET3469801582:TYPE.CONTRACT:ACC2PMEWZKPR34N3DEU10S2DM47SSBMXS2JVY6JGXV"
NodeURL = "ws://test.confluxrpc.com/ws"
BlockHash = "0x5379d291e1489dc9359d857c6fdaf44e673a109dfa813cc6460a8795566f60d3"
TransactionHash = "0x485404cac57cb1e58f1f3285d02044fc9722f5427ecc726149c9ea7468c6f64c"
BlockHashOfNewContract = "0x50da8bc04047c48aed189b96b8d907e145a3b919e8dd73bd3ca51a9aca86844a"
ERC20Address = "cfxtest:acfpjyyu1wp1h79h589dcxhms3z40zgj1y41cj8u3k"
14 changes: 8 additions & 6 deletions example/context/prepare.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ func initClient() {
var err error

keyStorePath := path.Join(currentDir, "keystore")
client, err = sdk.NewClient(config.NodeURL, sdk.ClientOption{KeystorePath: keyStorePath})
client, err = sdk.NewClient(config.NodeURL, sdk.ClientOption{
KeystorePath: keyStorePath,
RequestTimeout: time.Second * 10,
})
if err != nil {
panic(err)
}
Expand All @@ -80,11 +83,10 @@ func initClient() {

// init retry client
option := sdk.ClientOption{
KeystorePath: keyStorePath,
RetryCount: 10,
RetryInterval: time.Second,
// CallRpcLog: types.DefaultCallRPCLog,
// BatchCallRPCLog: types.DefaultBatchCallRPCLog,
KeystorePath: keyStorePath,
RetryCount: 10,
RetryInterval: time.Second,
RequestTimeout: time.Second * 10,
}
retryclient, err := sdk.NewClient(config.NodeURL, option)
if err != nil {
Expand Down
Loading

0 comments on commit f6846a2

Please sign in to comment.