为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
四季花海 2023-02-14 17:38:25
我正在尝试验证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。不确定我做错了什么。我在这里创建了一个围棋游乐场
查看完整描述

1 回答

?
翻过高山走不出你

TA贡献1875条经验 获得超3个赞

好的,我想通了。

在这里用解决方案去游乐场

我的 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

    }



查看完整回答
反对 回复 2023-02-14
  • 1 回答
  • 0 关注
  • 88 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信