无法创建在 Go 中以错误响应导致重试的处理程序

我正在尝试验证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)如果有错误,每个都应该采取时间=超时

  • 鉴于处理程序返回错误,它应该使重试尝试等于options.RetryMax = 6次数

我尝试调试代码,结果

// Attempt the request
resp, err = c.HTTPClient.Do(req.Request)

这里有 err as nil

不确定我做错了什么。

我在这里创建了一个围棋游乐场


四季花海
浏览 91回答 1
1回答

翻过高山走不出你

好的,我想通了。在这里用解决方案去游乐场我的 go 版本是 Go 1.17。如果您在 go playground(Go 版本 1.19)中运行上述代码,重试有效。对于 Go 1.17,retryable-http(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-http(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}最后,有以下选项。移动到 1.19尝试使用 go-retryablehttp v2.01如果您要使用 Go.17,请通过如下更新 CheckRetry 函数来创建自定义重试策略。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    }
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go