Support retry with multiple RPC endpoints
Some checks failed
CI / test (macos-11) (push) Has been cancelled
CI / test (ubuntu-20.04) (push) Has been cancelled

This commit is contained in:
JaydenLink
2025-06-03 16:45:01 +08:00
parent 9374f72fca
commit 1ddc7e0698
2 changed files with 46 additions and 26 deletions

View File

@@ -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
} }

View File

@@ -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)
} }