Support retry with multiple RPC endpoints
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"github.com/block-vision/sui-go-sdk/models"
|
"github.com/block-vision/sui-go-sdk/models"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -12,13 +13,18 @@ import (
|
|||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultTimeout = time.Second * 5
|
const (
|
||||||
|
defaultTimeout = time.Second * 5
|
||||||
|
defaultRetryCount = 3
|
||||||
|
)
|
||||||
|
|
||||||
type HttpConn struct {
|
type HttpConn struct {
|
||||||
c *http.Client
|
c *http.Client
|
||||||
rl *rate.Limiter
|
rl *rate.Limiter
|
||||||
rpcUrl string
|
rpcUrl string
|
||||||
timeout time.Duration
|
backupRPCURL []string
|
||||||
|
retryCount int
|
||||||
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDefaultRateLimiter() *rate.Limiter {
|
func newDefaultRateLimiter() *rate.Limiter {
|
||||||
@@ -26,11 +32,13 @@ func newDefaultRateLimiter() *rate.Limiter {
|
|||||||
return rateLimiter
|
return rateLimiter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHttpConn(rpcUrl string) *HttpConn {
|
func NewHttpConn(rpcUrl string, backupRPCs []string) *HttpConn {
|
||||||
return &HttpConn{
|
return &HttpConn{
|
||||||
c: &http.Client{},
|
c: &http.Client{},
|
||||||
rpcUrl: rpcUrl,
|
rpcUrl: rpcUrl,
|
||||||
timeout: defaultTimeout,
|
backupRPCURL: backupRPCs,
|
||||||
|
timeout: defaultTimeout,
|
||||||
|
retryCount: defaultRetryCount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,21 +62,33 @@ func (h *HttpConn) Request(ctx context.Context, op Operation) ([]byte, error) {
|
|||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
request, err := http.NewRequest("POST", h.rpcUrl, bytes.NewBuffer(reqBytes))
|
// 尝试所有可用的 RPC URL
|
||||||
if err != nil {
|
rpcURLs := append([]string{h.rpcUrl}, h.backupRPCURL...)
|
||||||
return []byte{}, err
|
var lastErr error
|
||||||
}
|
|
||||||
request = request.WithContext(ctx)
|
|
||||||
request.Header.Add("Content-Type", "application/json")
|
|
||||||
rsp, err := h.c.Do(request.WithContext(ctx))
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, err
|
|
||||||
}
|
|
||||||
defer rsp.Body.Close()
|
|
||||||
|
|
||||||
bodyBytes, err := ioutil.ReadAll(rsp.Body)
|
for _, rpc := range rpcURLs {
|
||||||
if err != nil {
|
request, err := http.NewRequest("POST", rpc, bytes.NewBuffer(reqBytes))
|
||||||
return []byte{}, err
|
if err != nil {
|
||||||
|
lastErr = fmt.Errorf("new request %s err: %v rpc: %s", op.Method, err, rpc)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
request = request.WithContext(ctx)
|
||||||
|
request.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rsp, err := h.c.Do(request.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
lastErr = fmt.Errorf("request %s err: %v rpc: %s", op.Method, err, rpc)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer rsp.Body.Close()
|
||||||
|
|
||||||
|
bodyBytes, err := ioutil.ReadAll(rsp.Body)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return bodyBytes, nil
|
||||||
}
|
}
|
||||||
return bodyBytes, nil
|
|
||||||
|
return []byte{}, lastErr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ type Client struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewSuiClient instantiates the Sui client to call the methods of each module.
|
// NewSuiClient instantiates the Sui client to call the methods of each module.
|
||||||
func NewSuiClient(rpcUrl string) ISuiAPI {
|
func NewSuiClient(mainRPC string, backupRPCs ...string) ISuiAPI {
|
||||||
conn := httpconn.NewHttpConn(rpcUrl)
|
conn := httpconn.NewHttpConn(mainRPC, backupRPCs)
|
||||||
return newClient(conn)
|
return newClient(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user