简体   繁体   English

如何重试HTTP POST请求

[英]How to retry HTTP POST requests

I have implemented the following code for retrying http post requests in Go. 我已经实现了以下代码,用于在Go中重试http发布请求。

In second retry attempt, I always get request body as null. 在第二次重试尝试中,我总是将请求正文设为null。 I have tried defer req.body.close() but it is not working. 我试过推迟req.body.close(),但是它不起作用。 Can anyone please help me this issue? 谁能帮我解决这个问题?

func httpRetry(req *http.Request, timeOut time.Duration, retryAttempts int, retryDelay time.Duration) (*http.Response, error) {

    attempts := 0

    for {
        attempts++
        fmt.Println("Attempt - ", attempts)
        statusCode := 0

        tr := &http.Transport{
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        }
        var netClient = &http.Client{
            Timeout:   time.Second * timeOut,
            Transport: tr,
        }
        fmt.Println("ret 88888 ", req)
        response, err := netClient.Do(req)

        //defer req.Body.Close()

        fmt.Println(response, "  dd", err)

        if response != nil {
            fmt.Println(response.StatusCode)
            statusCode = response.StatusCode
        }

        if err != nil {
            fmt.Println(err)
            //return response, err
        }

        if err == nil && statusCode == http.StatusOK {
            return response, nil
        }

        if err == nil && response != nil {
            defer req.Body.Close()
        }

        retry := retryDecision(statusCode, attempts, retryAttempts)
        if !retry {
            return response, err
        }

        fmt.Println("Retry Attempt number", attempts)
        time.Sleep(retryDelay * time.Second)
        fmt.Println("After Delay")
    }
}

func retryDecision(responseStatusCode int, attempts int, retryAttempts int) bool {

    retry := false
    fmt.Println("Retry Decision 0 ", responseStatusCode, attempts, retryAttempts)
    errorCodeForRetry :=
        []int{http.StatusInternalServerError, http.StatusUnauthorized, http.StatusNotImplemented, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout}

    for _, code := range errorCodeForRetry {
        if code == responseStatusCode && attempts <= retryAttempts {
            retry = true
        }
    }

    fmt.Println("Retry Decision ", retry)

    return retry

}

Please find error details below 请在下面找到错误详细信息

 Attempt - 1 ret 88888 &{POST https://localhost:8080/logs/ HTTP/1.1 1 1 map[] {{"converstnId":"","boId":"","msgId":"","serviceName":"","headers":"","properties":"","message":"","body":"aa","exceptionMessage":"","logType":"","exceptionStackTrace":""}} 

0x5ec890 170 [] false map[] map[] map[] 0x5ec890 170 []错误的map [] map [] map []
} }

 Attempt - 2 ret 88888 &{POST https://localhost:8080/logs/ HTTP/1.1 1 1 map[] {} 0x5ec890 170 [] false map[] map[] <nil> map[] 

} }

To be able to reuse a request whose body is non-nil, you first need to make sure that the body has been closed, not by you, but by the client's RoundTripper . 为了能够重用主体为非零的请求,首先需要确保主体(不是由您而是由客户端的RoundTripper)已关闭。

the relevant part of the docs: 文档的相关部分:

RoundTrip must always close the body, including on errors, but depending on the implementation may do so in a separate goroutine even after RoundTrip returns. RoundTrip必须始终关闭主体,包括发生错误时,但根据实现的不同,即使在RoundTrip返回之后,也可能在单独的goroutine中关闭它。 This means that callers wanting to reuse the body for subsequent requests must arrange to wait for the Close call before doing so. 这意味着希望重用主体以用于后续请求的调用者必须安排在等待Close调用之后再这样做。

When reusing requests that don't have a body, like GET, you still need to be careful: 重用没有正文的请求(如GET)时,您仍然需要小心:

If the request does not have a body, it can be reused as long as the caller does not mutate the Request until RoundTrip fails or the Response.Body is closed. 如果请求没有主体,则只要调用方在RoundTrip失败或Response.Body关闭之前不更改请求,就可以重用该请求。

Taken from here . 取自这里

So as long as you're sure that the RoundTripper closed the body, what you can do is to reset the body of the request at the top of each iteration. 因此,只要您确定RoundTripper关闭了主体,您可以做的就是在每次迭代的顶部重置请求的主体。 Something like this: 像这样:

func httpRetry(req *http.Request, timeOut time.Duration, retryAttempts int, retryDelay time.Duration) (*http.Response, error) {

    // ...

    data, err := ioutil.ReadAll(req.Body)
    if err != nil {
        return nil, err
    }

    // close the original body, we don't need it anymore
    if err := req.Body.Close(); err != nil {
        return err
    }

    for {

        req.Body = ioutil.NopCloser(bytes.NewReader(data)) // reset the body

        // ... your code ...

    }

    // ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM