Ошибка получения сообщения «Запрос уже отправлен» при использовании Polly - PullRequest
0 голосов
/ 25 февраля 2019

В настоящее время я использую Polly для ограничения количества отправляемых запросов.У меня есть такая политика:

private AsyncPolicyWrap<HttpResponseMessage> DefineAndRetrieveResiliencyStrategy()
{
    HttpStatusCode[] retryCodes = {
       HttpStatusCode.InternalServerError,
       HttpStatusCode.BadGateway,
       HttpStatusCode.GatewayTimeout
    };

    var waitAndRetryPolicy = Policy
        .HandleResult<HttpResponseMessage>(e => e.StatusCode == HttpStatusCode.ServiceUnavailable || e.StatusCode == (HttpStatusCode)429)
        .WaitAndRetryAsync(10,
            attempt => TimeSpan.FromSeconds(5), (exception, calculatedWaitDuration) =>
            {
                _log.Info($"Bitfinex API server is throttling our requests. Automatically delaying for {calculatedWaitDuration.TotalMilliseconds}ms");
            }
        );

    var circuitBreakerPolicyForRecoverable = Policy
        .Handle<HttpResponseException>()
        .OrResult<HttpResponseMessage>(r => retryCodes.Contains(r.StatusCode))
        .CircuitBreakerAsync(
            handledEventsAllowedBeforeBreaking: 3,
            durationOfBreak: TimeSpan.FromSeconds(3),
            onBreak: (outcome, breakDelay) =>
            {
                _log.Info($"Polly Circuit Breaker logging: Breaking the circuit for {breakDelay.TotalMilliseconds}ms due to: {outcome.Exception?.Message ?? outcome.Result.StatusCode.ToString()}");

            },
            onReset: () => _log.Info("Polly Circuit Breaker logging: Call ok... closed the circuit again"),
            onHalfOpen: () => _log.Info("Polly Circuit Breaker logging: Half-open: Next call is a trial")
        );

    return Policy.WrapAsync(waitAndRetryPolicy, circuitBreakerPolicyForRecoverable);
}

У меня есть следующий отправитель запроса:

private async Task<string> SendRequest(GenericRequest request, string httpMethod, string publicKey, string privateKey)
{
    var resiliencyStrategy = DefineAndRetrieveResiliencyStrategy();

    using (var client = new HttpClient())
    using (var httpRequest = new HttpRequestMessage(new HttpMethod(httpMethod), request.request))
    {
        string json = JsonConvert.SerializeObject(request);
        string json64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(json));
        byte[] data = Encoding.UTF8.GetBytes(json64);

        client.BaseAddress = new Uri(Properties.Settings.Default.BitfinexUri);

        var hashMaker = new HMACSHA384(Encoding.UTF8.GetBytes(privateKey));
        byte[] hash = hashMaker.ComputeHash(data);
        string signature = GetHexString(hash);

        httpRequest.Headers.Add("X-BFX-APIKEY", publicKey);
        httpRequest.Headers.Add("X-BFX-PAYLOAD", json64);
        httpRequest.Headers.Add("X-BFX-SIGNATURE", signature);

        var message = await resiliencyStrategy.ExecuteAsync(() => client.SendAsync(httpRequest));
        var response = message.Content.ReadAsStringAsync().Result;

        return response;
    }
}

Как только код попадет в waitAndRetryPolicy и ожидает необходимое количество времени, яполучить следующую ошибку:

System.InvalidOperationException: 'Сообщение с запросом уже отправлено.Не удается отправить одно и то же сообщение с запросом несколько раз. '

Я понимаю, что это происходит, потому что я снова отправляю тот же запрос HttpRequest, но не должна ли библиотека Polly справиться с такой проблемой?

1 Ответ

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

Это исключение:

System.InvalidOperationException: 'Сообщение с запросом уже отправлено.Невозможно отправить одно и то же сообщение-запрос несколько раз. '

выбрасывается внутренними компонентами HttpClient, если вы вызываете напрямую в любой .SendAsync (...)перегрузка с помощью сообщения HttpRequestMessage, которое уже было отправлено .

Если вы используете .NET Core, рекомендуется использовать Polly с HttpClientFactory : это решает указанное выше исключение, выполняя политику (например, повторная попытка) через DelegatingHandler в пределах HttpClient.Это также решает проблему истощения сокетов , которая может быть вызвана частым созданием / удалением HttpClient, к которому может быть уязвим код, размещенный в вопросе.

Если вы используете.NET Framework, рекомендуемые решения:

  • повторяют способ, которым HttpClientFactory размещает политику в DelegatingHandler; или
  • реорганизовать ваш код для создания нового экземпляра HttpRequestMessage (или клонировать существующий экземпляр) в коде, выполняемом с помощью политики.

В этом вопросе о стековом потоке подробно обсуждается проблема и множество вариантов вышеупомянутых решений.

...