首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >无法在Go中创建以错误响应的处理程序以导致重试

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

Stack Overflow用户
提问于 2022-09-17 05:57:49
回答 1查看 92关注 0票数 1

我试图验证go-retryablehttp执行是否按照指定的配置执行重试。

验证方法是创建一个测试

  • 创建可还原客户端
  • 创建一个新请求。
  • 使用错误处理程序创建新服务器。
  • 送达请求
  • 验证重试计数。

以上是我在下面的代码块中试图捕获的内容。

代码语言:javascript
运行
复制
//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)
   }
代码语言:javascript
运行
复制
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次数。

我试着调试代码,结果发现

代码语言:javascript
运行
复制
// Attempt the request
resp, err = c.HTTPClient.Do(req.Request)

这里犯了nil错误。

不知道我做错了什么。

我创建了一个围棋操场这里

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-09-18 05:02:53

好吧,我想出来了。

用解决方案这里去操场

我的围棋版本是go 1.17。如果您在go操场上运行上述代码( Go版本为1.19),则重试工作。

对于Go 1.17,retryable(v1.0.2)不处理状态错误代码

代码语言:javascript
运行
复制
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下的功能,如这里所示

代码语言:javascript
运行
复制
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函数创建一个自定义重试策略,如下所示。
代码语言:javascript
运行
复制
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
    }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73752481

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档