我试图验证go-retryablehttp执行是否按照指定的配置执行重试。
验证方法是创建一个测试
以上是我在下面的代码块中试图捕获的内容。
//function that returns 500 error
func InternalServerErrorHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("test_%d_body", http.StatusInternalServerError), http.StatusInternalServerError)
}
func TestCreateToolsClient(t *testing.T) {
//create a new server
ts := httptest.NewServer(http.HandlerFunc(InternalServerErrorHandler))
defer ts.Close()
//create a request
request, err := retryablehttp.NewRequest(http.MethodGet, ts.URL, nil)
if err != nil {
log.Fatal(err)
}
//create a retryable client
var options retryablehttp.Options
options.RetryWaitMin = 10 * time.Millisecond
options.RetryWaitMax = 50 * time.Millisecond
options.RetryMax = 6
options.Timeout = 60000 * time.Millisecond
retryableClient := retryablehttp.NewClient(options)
retryCount := -1
// to verify from stdout if the # of retry actually is getting counted
retryableClient.RequestLogHook = func(req *http.Request, retryNumber int) {
retryCount = retryNumber
log.Println("Retrying")
}
//execute the request
response, err := retryableClient.Do(request)
if err != nil {
return
}
//verify
require.Equal(t, http.StatusInternalServerError, response.StatusCode)
require.Equal(t, 2, retryCount)
}
我的理解是
retryableClient.Do(request)
都应该使用time=Timeout。options.RetryMax = 6
次数。我试着调试代码,结果发现
// Attempt the request
resp, err = c.HTTPClient.Do(req.Request)
这里犯了nil
错误。
不知道我做错了什么。
我创建了一个围棋操场这里
发布于 2022-09-18 05:02:53
好吧,我想出来了。
用解决方案这里去操场
我的围棋版本是go 1.17。如果您在go操场上运行上述代码( Go版本为1.19),则重试工作。
对于Go 1.17,retryable(v1.0.2)不处理状态错误代码
func DefaultRetryPolicy() func(ctx context.Context, resp *http.Response, err error) (bool, error) {
return func(ctx context.Context, resp *http.Response, err error) (bool, error) {
// do not retry on context.Canceled or context.DeadlineExceeded
//fmt.Printf("jkajsuiohsd %v\n", ctx.Err())
if ctx.Err() != nil {
return false, ctx.Err()
}
if err != nil {
if v, ok := err.(*url.Error); ok {
// Don't retry if the error was due to too many redirects.
if redirectsErrorRegex.MatchString(v.Error()) {
return false, nil
}
// Don't retry if the error was due to an invalid protocol scheme.
if schemeErrorRegex.MatchString(v.Error()) {
return false, nil
}
// Don't retry if the error was due to TLS cert verification failure.
if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
return false, nil
}
}
// The error is likely recoverable so retry.
return true, nil
}
//EXPECT HANDLING BASED ON STATUS CODES, BUT ABSENT
return false, nil
}
}
对于Go 1.19,retryable(v2.1)实现了baseRetryPolicy
下的功能,如这里所示
func baseRetryPolicy(resp *http.Response, err error) (bool, error) {
if err != nil {
if v, ok := err.(*url.Error); ok {
// Don't retry if the error was due to too many redirects.
if redirectsErrorRe.MatchString(v.Error()) {
return false, v
}
// Don't retry if the error was due to an invalid protocol scheme.
if schemeErrorRe.MatchString(v.Error()) {
return false, v
}
// Don't retry if the error was due to TLS cert verification failure.
if notTrustedErrorRe.MatchString(v.Error()) {
return false, v
}
if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
return false, v
}
}
// The error is likely recoverable so retry.
return true, nil
}
// 429 Too Many Requests is recoverable. Sometimes the server puts
// a Retry-After response header to indicate when the server is
// available to start processing request from client.
if resp.StatusCode == http.StatusTooManyRequests {
return true, nil
}
// Check the response code. We retry on 500-range responses to allow
// the server time to recover, as 500's are typically not permanent
// errors and may relate to outages on the server side. This will catch
// invalid response codes as well, like 0 and 999.
//THIS PART HERE FLAGS RETRIES ON STATUS CODES!
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != http.StatusNotImplemented) {
return true, fmt.Errorf("unexpected HTTP status %s", resp.Status)
}
return false, nil
}
最后,有以下几种选择。
func TestCreateToolsClient(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(InternalServerErrorHandler))
defer ts.Close()
request, err := retryablehttp.NewRequest(http.MethodGet, ts.URL, nil)
if err != nil {
log.Fatal(err)
}
var options retryablehttp.Options
options.RetryWaitMin = 10 * time.Millisecond
options.RetryWaitMax = 50 * time.Millisecond
options.RetryMax = 6
options.Timeout = 60 * time.Second
//options.Timeout = 30000 * time.Millisecond
retryableClient := retryablehttp.NewClient(options)
retryCount := -1
// to verify from stdout if the # of retry actually is getting counted
retryableClient.RequestLogHook = func(req *http.Request, retryNumber int) {
retryCount = retryNumber
log.Println("Retrying")
}
// A regular expression to match the error returned by net/http when the
// configured number of redirects is exhausted. This error isn't typed
// specifically so we resort to matching on the error string.
redirectsErrorRe := regexp.MustCompile(`stopped after \d+ redirects\z`)
// A regular expression to match the error returned by net/http when the
// scheme specified in the URL is invalid. This error isn't typed
// specifically so we resort to matching on the error string.
schemeErrorRe := regexp.MustCompile(`unsupported protocol scheme`)
// A regular expression to match the error returned by net/http when the
// TLS certificate is not trusted. This error isn't typed
// specifically so we resort to matching on the error string.
notTrustedErrorRe := regexp.MustCompile(`certificate is not trusted`)
retryableClient.CheckRetry = func(_ context.Context, resp *http.Response, err error) (bool, error) {
if err != nil {
if v, ok := err.(*url.Error); ok {
// Don't retry if the error was due to too many redirects.
if redirectsErrorRe.MatchString(v.Error()) {
return false, v
}
// Don't retry if the error was due to an invalid protocol scheme.
if schemeErrorRe.MatchString(v.Error()) {
return false, v
}
// Don't retry if the error was due to TLS cert verification failure.
if notTrustedErrorRe.MatchString(v.Error()) {
return false, v
}
if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
return false, v
}
}
// The error is likely recoverable so retry.
return true, nil
}
// 429 Too Many Requests is recoverable. Sometimes the server puts
// a Retry-After response header to indicate when the server is
// available to start processing request from client.
if resp.StatusCode == http.StatusTooManyRequests {
return true, nil
}
// Check the response code. We retry on 500-range responses to allow
// the server time to recover, as 500's are typically not permanent
// errors and may relate to outages on the server side. This will catch
// invalid response codes as well, like 0 and 999.
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != http.StatusNotImplemented) {
return true, fmt.Errorf("unexpected HTTP status %s", resp.Status)
}
return false, nil
}
https://stackoverflow.com/questions/73752481
复制相似问题