Как повторить запросы HTTP POST - PullRequest
0 голосов
/ 15 февраля 2019

Я реализовал следующий код для повторной отправки http-запросов в Go.

Во второй попытке повторной попытки я всегда получаю тело запроса как нулевое.Я попытался отложить req.body.close (), но он не работает.Может кто-нибудь, пожалуйста, помогите мне в этом вопросе?

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

}

Ниже приведена подробная информация об ошибке

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 []
}

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

}

1 Ответ

0 голосов
/ 15 февраля 2019

Чтобы иметь возможность повторно использовать запрос, тело которого не равно нулю, сначала нужно убедиться, что тело было закрыто не вами, а клиентом RoundTripper .

соответствующая часть документов:

RoundTrip всегда должен закрывать тело, в том числе при ошибках, но в зависимости от реализации может сделать это в отдельной программе даже после возврата RoundTrip.Это означает, что вызывающие абоненты, желающие повторно использовать тело для последующих запросов, должны организовать ожидание вызова Close, прежде чем делать это.

При повторном использовании запросов, которые не имеют тела, таких как GET, вам все равно нужночтобы быть осторожным:

Если у запроса нет тела, его можно использовать повторно до тех пор, пока вызывающая сторона не изменит запрос до тех пор, пока не завершится ошибка RoundTrip или не будет закрыт Response.Body.

Взято из здесь .

Так что, если вы уверены, что RoundTripper закрыл тело, вы можете сбросить тело запроса ввершина каждой итерации.Примерно так:

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 ...

    }

    // ...
}
...