github.com/qiniu/go-sdk/v7@v7.27.0/internal/clientv2/interceptor_retry_simple.go (about) 1 package clientv2 2 3 import ( 4 "context" 5 "io" 6 "math/rand" 7 "net" 8 "net/http" 9 "sync" 10 "time" 11 12 clientv1 "github.com/qiniu/go-sdk/v7/client" 13 internal_io "github.com/qiniu/go-sdk/v7/internal/io" 14 "github.com/qiniu/go-sdk/v7/storagev2/backoff" 15 "github.com/qiniu/go-sdk/v7/storagev2/chooser" 16 "github.com/qiniu/go-sdk/v7/storagev2/resolver" 17 "github.com/qiniu/go-sdk/v7/storagev2/retrier" 18 ) 19 20 type ( 21 bufferResponseContextKey struct{} 22 23 SimpleRetryConfig struct { 24 RetryMax int // 最大重试次数 25 RetryInterval func() time.Duration // 重试时间间隔 v1 26 Backoff backoff.Backoff // 重试时间间隔 v2,优先级高于 RetryInterval 27 ShouldRetry func(req *http.Request, resp *http.Response, err error) bool 28 Resolver resolver.Resolver // 主备域名解析器 29 Chooser chooser.Chooser // IP 选择器 30 Retrier retrier.Retrier // 重试器 31 32 BeforeResolve func(*http.Request) // 域名解析前回调函数 33 AfterResolve func(*http.Request, []net.IP) // 域名解析后回调函数 34 ResolveError func(*http.Request, error) // 域名解析错误回调函数 35 BeforeBackoff func(*http.Request, *retrier.RetrierOptions, time.Duration) // 退避前回调函数 36 AfterBackoff func(*http.Request, *retrier.RetrierOptions, time.Duration) // 退避后回调函数 37 BeforeRequest func(*http.Request, *retrier.RetrierOptions) // 请求前回调函数 38 AfterResponse func(*http.Response, *retrier.RetrierOptions, error) // 请求后回调函数 39 } 40 41 simpleRetryInterceptor struct { 42 config SimpleRetryConfig 43 } 44 ) 45 46 func (c *SimpleRetryConfig) init() { 47 if c == nil { 48 return 49 } 50 51 if c.RetryMax < 0 { 52 c.RetryMax = 0 53 } 54 } 55 56 func (c *SimpleRetryConfig) getRetryInterval(ctx context.Context, attempts int) time.Duration { 57 if bf := c.Backoff; bf != nil { 58 return bf.Time(ctx, &backoff.BackoffOptions{Attempts: attempts}) 59 } 60 if ri := c.RetryInterval; ri != nil { 61 return ri() 62 } 63 return defaultRetryInterval() 64 } 65 66 var errorRetrier = retrier.NewErrorRetrier() 67 68 func (c *SimpleRetryConfig) getRetryDecision(req *http.Request, resp *http.Response, err error, attempts int) retrier.RetryDecision { 69 if c.ShouldRetry != nil { 70 if c.ShouldRetry(req, resp, err) { 71 return retrier.RetryRequest 72 } else { 73 return retrier.DontRetry 74 } 75 } else { 76 r := errorRetrier 77 if c.Retrier != nil { 78 r = c.Retrier 79 } 80 return r.Retry(resp, err, &retrier.RetrierOptions{Attempts: attempts}) 81 } 82 } 83 84 func NewSimpleRetryInterceptor(config SimpleRetryConfig) Interceptor { 85 return &simpleRetryInterceptor{config: config} 86 } 87 88 func (interceptor *simpleRetryInterceptor) Priority() InterceptorPriority { 89 return InterceptorPriorityRetrySimple 90 } 91 92 func (interceptor *simpleRetryInterceptor) Intercept(req *http.Request, handler Handler) (resp *http.Response, err error) { 93 var chosenIPs []net.IP 94 95 if interceptor == nil || req == nil { 96 return interceptor.callHandler(req, &retrier.RetrierOptions{Attempts: 0}, handler) 97 } 98 99 interceptor.config.init() 100 101 hostname := req.URL.Hostname() 102 resolvedIPs := interceptor.resolve(req, hostname, false) 103 resolvedIPsMap := make(map[string]struct{}, len(resolvedIPs)) 104 addIPsToMap(resolvedIPsMap, resolvedIPs) 105 106 // 可能会被重试多次 107 for i := 0; ; i++ { 108 // Clone 防止后面 Handler 处理对 req 有污染 109 reqBefore := cloneReq(req) 110 req, chosenIPs = interceptor.ensureChoose(req, hostname, resolvedIPs, resolvedIPsMap) 111 resp, err = interceptor.callHandler(req, &retrier.RetrierOptions{Attempts: i}, handler) 112 113 if err == nil && resp.StatusCode/100 >= 4 { 114 err = clientv1.ResponseError(resp) 115 } 116 117 retryDecision := interceptor.config.getRetryDecision(reqBefore, resp, err, i) 118 if retryDecision == retrier.DontRetry { 119 interceptor.feedbackGood(req, hostname, chosenIPs) 120 return resp, err 121 } 122 interceptor.feedbackBad(req, hostname, chosenIPs) 123 124 req = reqBefore 125 126 if retryDecision == retrier.TryNextHost || i >= interceptor.config.RetryMax { 127 break 128 } 129 130 if req.Body != nil && req.GetBody != nil { 131 if closer, ok := req.Body.(io.Closer); ok { 132 closer.Close() 133 } 134 if req.Body, err = req.GetBody(); err != nil { 135 return 136 } 137 } 138 139 if resp != nil && resp.Body != nil { 140 _ = internal_io.SinkAll(resp.Body) 141 resp.Body.Close() 142 } 143 144 interceptor.wait(req, i) 145 } 146 return resp, err 147 } 148 149 func (interceptor *simpleRetryInterceptor) ensureChoose(req *http.Request, hostname string, resolvedIPs []net.IP, resolvedIPsMap map[string]struct{}) (*http.Request, []net.IP) { 150 var chosenIPs []net.IP 151 beforeChoose: 152 req, chosenIPs = interceptor.choose(req, resolvedIPs, hostname, true) 153 for len(chosenIPs) == 0 { 154 newResolvedIPs := interceptor.resolve(req, hostname, true) 155 if len(newResolvedIPs) > 0 && !isAllIPsInMap(resolvedIPsMap, newResolvedIPs) { // 但凡能拿到新的 IP 就尝试新的 IP 156 addIPsToMap(resolvedIPsMap, newResolvedIPs) 157 resolvedIPs = mergeIPs(resolvedIPs, newResolvedIPs) 158 goto beforeChoose 159 } else { // 如果拿不到新的 IP,只能从已有的 IP 集合里选择 160 req, chosenIPs = interceptor.choose(req, resolvedIPs, hostname, false) 161 break 162 } 163 } 164 return req, chosenIPs 165 } 166 167 func (interceptor *simpleRetryInterceptor) callHandler(req *http.Request, options *retrier.RetrierOptions, handler Handler) (resp *http.Response, err error) { 168 if interceptor.config.BeforeRequest != nil { 169 interceptor.config.BeforeRequest(req, options) 170 } 171 resp, err = handler(req) 172 if interceptor.config.AfterResponse != nil { 173 interceptor.config.AfterResponse(resp, options, err) 174 } 175 return 176 } 177 178 var ( 179 defaultResolver resolver.Resolver 180 defaultResolverOnce sync.Once 181 ) 182 183 func (interceptor *simpleRetryInterceptor) resolver() resolver.Resolver { 184 var r resolver.Resolver 185 if r = interceptor.config.Resolver; r == nil { 186 defaultResolverOnce.Do(func() { 187 defaultResolver, _ = resolver.NewCacheResolver(nil, nil) 188 }) 189 r = defaultResolver 190 } 191 return r 192 } 193 194 func (interceptor *simpleRetryInterceptor) resolve(req *http.Request, hostname string, bypassCache bool) []net.IP { 195 var ( 196 ctx context.Context 197 ips []net.IP 198 err error 199 ) 200 if r := interceptor.resolver(); r != nil { 201 if interceptor.config.BeforeResolve != nil { 202 interceptor.config.BeforeResolve(req) 203 } 204 ctx = req.Context() 205 if bypassCache { 206 ctx = resolver.WithByPassResolverCache(ctx) 207 } 208 if ips, err = r.Resolve(ctx, hostname); err == nil { 209 if interceptor.config.AfterResolve != nil { 210 interceptor.config.AfterResolve(req, ips) 211 } 212 } else if interceptor.config.ResolveError != nil { 213 interceptor.config.ResolveError(req, err) 214 } 215 } 216 return ips 217 } 218 219 var ( 220 defaultChooser chooser.Chooser 221 defaultChooserOnce sync.Once 222 ) 223 224 func (interceptor *simpleRetryInterceptor) chooser() chooser.Chooser { 225 var cs chooser.Chooser 226 if cs = interceptor.config.Chooser; cs == nil { 227 defaultChooserOnce.Do(func() { 228 defaultChooser = chooser.NewShuffleChooser(chooser.NewSmartIPChooser(nil)) 229 }) 230 cs = defaultChooser 231 } 232 return cs 233 } 234 235 func (interceptor *simpleRetryInterceptor) choose(req *http.Request, ips []net.IP, hostname string, failFast bool) (*http.Request, []net.IP) { 236 if len(ips) > 0 { 237 ips = interceptor.chooser().Choose(req.Context(), ips, &chooser.ChooseOptions{Domain: hostname, FailFast: failFast}) 238 if len(ips) > 0 { 239 req = req.WithContext(clientv1.WithResolvedIPs(req.Context(), hostname, ips)) 240 } 241 } 242 return req, ips 243 } 244 245 func (interceptor *simpleRetryInterceptor) feedbackGood(req *http.Request, hostname string, ips []net.IP) { 246 if len(ips) > 0 { 247 interceptor.resolver().FeedbackGood(req.Context(), hostname, ips) 248 interceptor.chooser().FeedbackGood(req.Context(), ips, &chooser.FeedbackOptions{Domain: hostname}) 249 } 250 } 251 252 func (interceptor *simpleRetryInterceptor) feedbackBad(req *http.Request, hostname string, ips []net.IP) { 253 if len(ips) > 0 { 254 interceptor.resolver().FeedbackBad(req.Context(), hostname, ips) 255 interceptor.chooser().FeedbackBad(req.Context(), ips, &chooser.FeedbackOptions{Domain: hostname}) 256 } 257 } 258 259 func (interceptor *simpleRetryInterceptor) wait(req *http.Request, attempts int) { 260 retryInterval := interceptor.config.getRetryInterval(req.Context(), attempts) 261 if interceptor.config.BeforeBackoff != nil { 262 interceptor.config.BeforeBackoff(req, &retrier.RetrierOptions{Attempts: attempts}, retryInterval) 263 } 264 if retryInterval >= time.Microsecond { 265 time.Sleep(retryInterval) 266 } 267 if interceptor.config.AfterBackoff != nil { 268 interceptor.config.AfterBackoff(req, &retrier.RetrierOptions{Attempts: attempts}, retryInterval) 269 } 270 } 271 272 func bufferResponse(resp *http.Response) error { 273 buffer, err := internal_io.ReadAll(resp.Body) 274 if err != nil { 275 return err 276 } 277 resp.Body = internal_io.NewBytesNopCloser(buffer) 278 return nil 279 } 280 281 func defaultRetryInterval() time.Duration { 282 return time.Duration(50+rand.Int()%50) * time.Millisecond 283 } 284 285 func addIPsToMap(m map[string]struct{}, ips []net.IP) { 286 for _, ip := range ips { 287 m[string(ip.To16())] = struct{}{} 288 } 289 } 290 291 func isAllIPsInMap(m map[string]struct{}, ips []net.IP) bool { 292 for _, ip := range ips { 293 if _, ok := m[string(ip.To16())]; !ok { 294 return false 295 } 296 } 297 return true 298 } 299 300 func mergeIPs(ips1, ips2 []net.IP) []net.IP { 301 newIPs := make([]net.IP, 0, len(ips1)+len(ips2)) 302 newIPs = append(newIPs, ips1...) 303 loop: 304 for _, ip2 := range ips2 { 305 for _, ip1 := range ips1 { 306 if ip1.Equal(ip2) { 307 continue loop 308 } 309 } 310 newIPs = append(newIPs, ip2) 311 } 312 return newIPs 313 }