【问题标题】:Receiving error 'The request message was already sent' when using Polly使用 Polly 时收到错误“请求消息已发送”
【发布时间】:2019-02-25 16:14:25
【问题描述】:

我目前正在使用 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 库不应该处理这样的问题吗?

【问题讨论】:

    标签: c# httprequest httpresponse polly


    【解决方案1】:

    那个例外:

    System.InvalidOperationException: '请求消息已经发送。不能多次发送相同的请求消息。'

    如果您是call directly into any .SendAsync(...) overload with an HttpRequestMessage which has already been sent,则为thrown by the internals of HttpClient

    如果您使用的是 .NET Core,推荐的解决方案是使用Polly with HttpClientFactory:这通过在HttpClient 中通过DelegatingHandler 执行策略(例如重试)来解决上述异常。它还解决了socket-exhaustion problem 可能由频繁创建/处置HttpClient 引起的问题,问题中发布的代码可能容易受到攻击。

    如果您使用 .NET 框架,推荐的解决方案是:

    • 复制 HttpClientFactory 将策略放置在 DelegatingHandler 中的方式;
    • 重构您的代码以在通过策略执行的代码中制造HttpRequestMessage 的新实例(或克隆现有实例)。

    This stackoverflow question 广泛讨论了这个问题以及上述解决方案的许多变体。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多