github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/retrier/retrier.go (about) 1 package retrier 2 3 import ( 4 "context" 5 "errors" 6 "io" 7 "net" 8 "net/http" 9 "net/url" 10 "os" 11 "strings" 12 "syscall" 13 14 clientv1 "github.com/qiniu/go-sdk/v7/client" 15 "github.com/qiniu/go-sdk/v7/storagev2/backoff" 16 ) 17 18 type ( 19 // RetryDecision 重试决策 20 RetryDecision int 21 // RetrierOptions 重试器选项 22 RetrierOptions backoff.BackoffOptions 23 24 // Retrier 重试器接口 25 Retrier interface { 26 // Retry 判断是否重试,如何重试 27 Retry(*http.Response, error, *RetrierOptions) RetryDecision 28 } 29 30 neverRetrier struct{} 31 errorRetrier struct{} 32 customizedRetrier struct { 33 retryFn func(*http.Response, error, *RetrierOptions) RetryDecision 34 } 35 ) 36 37 const ( 38 // 不再重试 39 DontRetry RetryDecision = iota 40 41 // 重试下一个域名 42 TryNextHost 43 44 // 重试当前域名 45 RetryRequest 46 ) 47 48 // NewRetrier 创建自定义重试器 49 func NewRetrier(fn func(*http.Response, error, *RetrierOptions) RetryDecision) Retrier { 50 return customizedRetrier{retryFn: fn} 51 } 52 53 func (retrier customizedRetrier) Retry(response *http.Response, err error, options *RetrierOptions) RetryDecision { 54 return retrier.retryFn(response, err, options) 55 } 56 57 // NewNeverRetrier 创建从不重试的重试器 58 func NewNeverRetrier() Retrier { 59 return neverRetrier{} 60 } 61 62 func (neverRetrier) Retry(*http.Response, error, *RetrierOptions) RetryDecision { 63 return DontRetry 64 } 65 66 // NewErrorRetrier 创建错误重试器,为七牛默认的错误重试器 67 func NewErrorRetrier() Retrier { 68 return errorRetrier{} 69 } 70 71 func (errorRetrier) Retry(response *http.Response, err error, _ *RetrierOptions) RetryDecision { 72 if isResponseRetryable(response) { 73 return RetryRequest 74 } else if err == nil { 75 return DontRetry 76 } else { 77 return getRetryDecisionForError(err) 78 } 79 } 80 81 func isResponseRetryable(resp *http.Response) bool { 82 if resp == nil { 83 return false 84 } 85 return IsStatusCodeRetryable(resp.StatusCode) 86 } 87 88 func IsStatusCodeRetryable(statusCode int) bool { 89 if statusCode < 500 { 90 return false 91 } 92 93 if statusCode == 501 || statusCode == 509 || statusCode == 579 || 94 statusCode == 608 || statusCode == 612 || statusCode == 614 || statusCode == 616 || statusCode == 618 || 95 statusCode == 630 || statusCode == 631 || statusCode == 632 || statusCode == 640 || statusCode == 701 { 96 return false 97 } 98 99 return true 100 } 101 102 func IsErrorRetryable(err error) bool { 103 if err == nil { 104 return false 105 } 106 107 switch getRetryDecisionForError(err) { 108 case RetryRequest, TryNextHost: 109 return true 110 default: 111 return false 112 } 113 } 114 115 var ErrMaliciousResponse = errors.New("malicious response") 116 117 func getRetryDecisionForError(err error) RetryDecision { 118 if err == nil { 119 return DontRetry 120 } 121 122 tryToUnwrapUnderlyingError := func(err error) (error, bool) { 123 switch err := err.(type) { 124 case *os.PathError: 125 return err.Err, true 126 case *os.LinkError: 127 return err.Err, true 128 case *os.SyscallError: 129 return err.Err, true 130 case *url.Error: 131 return err.Err, true 132 case *net.OpError: 133 return err.Err, true 134 } 135 return err, false 136 } 137 unwrapUnderlyingError := func(err error) error { 138 ok := true 139 for ok { 140 err, ok = tryToUnwrapUnderlyingError(err) 141 } 142 return err 143 } 144 145 unwrapedErr := unwrapUnderlyingError(err) 146 if unwrapedErr == context.DeadlineExceeded { 147 return DontRetry 148 } else if unwrapedErr == ErrMaliciousResponse { 149 return RetryRequest 150 } else if os.IsTimeout(unwrapedErr) { 151 return RetryRequest 152 } else if unwrapedErr == io.ErrUnexpectedEOF { 153 return RetryRequest 154 } else if dnsError, ok := unwrapedErr.(*net.DNSError); ok && isDnsNotFoundError(dnsError) { 155 return TryNextHost 156 } else if syscallError, ok := unwrapedErr.(*os.SyscallError); ok { 157 switch syscallError.Err { 158 case syscall.ECONNREFUSED, syscall.ECONNABORTED, syscall.ECONNRESET: 159 return TryNextHost 160 default: 161 return DontRetry 162 } 163 } else if errno, ok := unwrapedErr.(syscall.Errno); ok { 164 switch errno { 165 case syscall.ECONNREFUSED, syscall.ECONNABORTED, syscall.ECONNRESET: 166 return TryNextHost 167 default: 168 return DontRetry 169 } 170 } else if unwrapedErr == context.Canceled { 171 return DontRetry 172 } else if clientErr, ok := unwrapedErr.(*clientv1.ErrorInfo); ok { 173 if clientErr.Code == http.StatusBadRequest && strings.Contains(unwrapedErr.Error(), "transfer acceleration is not configured on this bucket") { 174 return TryNextHost 175 } else if IsStatusCodeRetryable(clientErr.Code) { 176 return RetryRequest 177 } else { 178 return DontRetry 179 } 180 } 181 desc := unwrapedErr.Error() 182 if strings.Contains(desc, "use of closed network connection") || 183 strings.Contains(desc, "unexpected EOF reading trailer") || 184 strings.Contains(desc, "transport connection broken") || 185 strings.Contains(desc, "server closed idle connection") { 186 return RetryRequest 187 } 188 return DontRetry 189 }