You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-library/vendor/github.com/qiniu/go-sdk/v7/internal/clientv2/interceptor_retry_simple.go

183 lines
3.7 KiB

package clientv2
import (
clientv1 "github.com/qiniu/go-sdk/v7/client"
"io"
"math/rand"
"net"
"net/http"
"net/url"
"os"
"syscall"
"time"
)
type RetryConfig struct {
RetryMax int // 最大重试次数
RetryInterval func() time.Duration // 重试时间间隔
ShouldRetry func(req *http.Request, resp *http.Response, err error) bool
}
func (c *RetryConfig) init() {
if c == nil {
return
}
if c.RetryMax < 0 {
c.RetryMax = 0
}
if c.RetryInterval == nil {
c.RetryInterval = func() time.Duration {
return time.Duration(50+rand.Int()%50) * time.Millisecond
}
}
if c.ShouldRetry == nil {
c.ShouldRetry = func(req *http.Request, resp *http.Response, err error) bool {
return isSimpleRetryable(req, resp, err)
}
}
}
type simpleRetryInterceptor struct {
config RetryConfig
}
func NewSimpleRetryInterceptor(config RetryConfig) Interceptor {
return &simpleRetryInterceptor{
config: config,
}
}
func (interceptor *simpleRetryInterceptor) Priority() InterceptorPriority {
return InterceptorPriorityRetrySimple
}
func (interceptor *simpleRetryInterceptor) Intercept(req *http.Request, handler Handler) (resp *http.Response, err error) {
if interceptor == nil || req == nil {
return handler(req)
}
interceptor.config.init()
// 不重试
if interceptor.config.RetryMax <= 0 {
return handler(req)
}
// 可能会被重试多次
for i := 0; ; i++ {
// Clone 防止后面 Handler 处理对 req 有污染
reqBefore := cloneReq(req.Context(), req)
resp, err = handler(req)
if !interceptor.config.ShouldRetry(reqBefore, resp, err) {
return resp, err
}
req = reqBefore
if i >= interceptor.config.RetryMax {
break
}
retryInterval := interceptor.config.RetryInterval()
if retryInterval < time.Microsecond {
continue
}
time.Sleep(retryInterval)
}
return resp, err
}
func isSimpleRetryable(req *http.Request, resp *http.Response, err error) bool {
return isRequestRetryable(req) && (isResponseRetryable(resp) || IsErrorRetryable(err))
}
func isRequestRetryable(req *http.Request) bool {
if req == nil {
return false
}
if req.Body == nil {
return true
}
if req.GetBody != nil {
b, err := req.GetBody()
if err != nil || b == nil {
return false
}
req.Body = b
return true
}
seeker, ok := req.Body.(io.Seeker)
if !ok {
return false
}
_, err := seeker.Seek(0, io.SeekStart)
return err == nil
}
func isResponseRetryable(resp *http.Response) bool {
if resp == nil {
return false
}
return isStatusCodeRetryable(resp.StatusCode)
}
func isStatusCodeRetryable(statusCode int) bool {
if statusCode < 500 {
return false
}
if statusCode == 501 || statusCode == 509 || statusCode == 573 || statusCode == 579 ||
statusCode == 608 || statusCode == 612 || statusCode == 614 || statusCode == 616 || statusCode == 618 ||
statusCode == 630 || statusCode == 631 || statusCode == 632 || statusCode == 640 || statusCode == 701 {
return false
}
return true
}
func IsErrorRetryable(err error) bool {
if err == nil {
return false
}
switch t := err.(type) {
case *net.OpError:
return isNetworkErrorWithOpError(t)
case *url.Error:
return IsErrorRetryable(t.Err)
case net.Error:
return t.Timeout()
case *clientv1.ErrorInfo:
return isStatusCodeRetryable(t.Code)
default:
return false
}
}
func isNetworkErrorWithOpError(err *net.OpError) bool {
if err == nil {
return false
}
switch t := err.Err.(type) {
case *net.DNSError:
return true
case *os.SyscallError:
if errno, ok := t.Err.(syscall.Errno); ok {
return errno == syscall.ECONNABORTED ||
errno == syscall.ECONNRESET ||
errno == syscall.ECONNREFUSED ||
errno == syscall.ETIMEDOUT
}
}
return false
}