github.com/qiniu/go-sdk/v7@v7.27.0/internal/clientv2/interceptor_retry_hosts.go (about) 1 package clientv2 2 3 import ( 4 "io" 5 "net/http" 6 "net/url" 7 "strings" 8 "time" 9 10 "github.com/qiniu/go-sdk/v7/internal/hostprovider" 11 internal_io "github.com/qiniu/go-sdk/v7/internal/io" 12 "github.com/qiniu/go-sdk/v7/storagev2/retrier" 13 ) 14 15 type HostsRetryConfig struct { 16 RetryMax int // 最大重试次数 17 ShouldRetry func(req *http.Request, resp *http.Response, err error) bool 18 Retrier retrier.Retrier // 重试器 19 HostFreezeDuration time.Duration // 主备域名冻结时间(默认:600s),当一个域名请求失败被冻结的时间,最小 time.Millisecond 20 HostProvider hostprovider.HostProvider // 备用域名获取方法 21 ShouldFreezeHost func(req *http.Request, resp *http.Response, err error) bool 22 } 23 24 func (c *HostsRetryConfig) init() { 25 if c.RetryMax < 0 { 26 c.RetryMax = 1 27 } 28 29 if c.HostFreezeDuration < time.Millisecond { 30 c.HostFreezeDuration = 600 * time.Second 31 } 32 33 if c.ShouldFreezeHost == nil { 34 c.ShouldFreezeHost = func(req *http.Request, resp *http.Response, err error) bool { 35 return true 36 } 37 } 38 } 39 40 func (c *HostsRetryConfig) getRetryDecision(req *http.Request, resp *http.Response, err error, attempts int) retrier.RetryDecision { 41 if c.ShouldRetry != nil { 42 if c.ShouldRetry(req, resp, err) { 43 return retrier.RetryRequest 44 } else { 45 return retrier.DontRetry 46 } 47 } else if c.Retrier != nil { 48 return c.Retrier.Retry(resp, err, &retrier.RetrierOptions{Attempts: attempts}) 49 } else { 50 return errorRetrier.Retry(resp, err, &retrier.RetrierOptions{Attempts: attempts}) 51 } 52 } 53 54 type hostsRetryInterceptor struct { 55 options HostsRetryConfig 56 } 57 58 func NewHostsRetryInterceptor(options HostsRetryConfig) Interceptor { 59 return &hostsRetryInterceptor{ 60 options: options, 61 } 62 } 63 64 func (interceptor *hostsRetryInterceptor) Priority() InterceptorPriority { 65 return InterceptorPriorityRetryHosts 66 } 67 68 func (interceptor *hostsRetryInterceptor) Intercept(req *http.Request, handler Handler) (resp *http.Response, err error) { 69 if interceptor == nil || req == nil { 70 return handler(req) 71 } 72 73 interceptor.options.init() 74 75 // 不重试 76 if interceptor.options.RetryMax <= 0 { 77 return handler(req) 78 } 79 80 for i := 0; ; i++ { 81 // Clone 防止后面 Handler 处理对 req 有污染 82 reqBefore := cloneReq(req) 83 84 resp, err = handler(req) 85 86 retryDecision := interceptor.options.getRetryDecision(reqBefore, resp, err, i) 87 if retryDecision == retrier.DontRetry { 88 return resp, err 89 } 90 91 // 尝试冻结域名 92 oldHost := req.URL.Host 93 if interceptor.options.ShouldFreezeHost(req, resp, err) { 94 if fErr := interceptor.options.HostProvider.Freeze(oldHost, err, interceptor.options.HostFreezeDuration); fErr != nil { 95 break 96 } 97 } 98 99 if i >= interceptor.options.RetryMax { 100 break 101 } 102 103 // 尝试更换域名 104 newHost, pErr := interceptor.options.HostProvider.Provider() 105 if pErr != nil { 106 break 107 } 108 if index := strings.Index(newHost, "://"); index >= 0 { 109 newHost = newHost[(index + len("://")):] 110 } 111 if index := strings.Index(newHost, "/"); index >= 0 { 112 newHost = newHost[:index] 113 } 114 if len(newHost) == 0 { 115 break 116 } 117 118 if newHost != oldHost { 119 urlString := req.URL.String() 120 urlString = strings.Replace(urlString, oldHost, newHost, 1) 121 u, ppErr := url.Parse(urlString) 122 if ppErr != nil { 123 break 124 } 125 126 reqBefore.Host = u.Host 127 reqBefore.URL = u 128 } 129 130 req = reqBefore 131 132 if req.Body != nil && req.GetBody != nil { 133 if closer, ok := req.Body.(io.Closer); ok { 134 closer.Close() 135 } 136 if req.Body, err = req.GetBody(); err != nil { 137 return 138 } 139 } 140 141 if resp != nil && resp.Body != nil { 142 _ = internal_io.SinkAll(resp.Body) 143 resp.Body.Close() 144 } 145 } 146 return resp, err 147 }