Polly WaitAndRetryAsync 在一次重试后挂起

Tud*_*dor 4 .net c# polly exponential-backoff retry-logic

如果 HTTP 调用失败,我在非常基本的场景中使用 Polly 进行指数退避:

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
    return await HandleTransientHttpError()
        .Or<TimeoutException>()
        .WaitAndRetryAsync(4, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3, retryAttempt)))
        .ExecuteAsync(async () => await base.SendAsync(request, cancellationToken).ConfigureAwait(false));
}

private static PolicyBuilder<HttpResponseMessage> HandleTransientHttpError()
{
    return Policy
        .HandleResult<HttpResponseMessage>(response => (int)response.StatusCode >= 500 || response.StatusCode == System.Net.HttpStatusCode.RequestTimeout)
        .Or<HttpRequestException>();
}
Run Code Online (Sandbox Code Playgroud)

我有一个测试 API,它只是HttpListenerwhile(true). 目前,我正在尝试测试客户端在每次调用收到 500 时是否正确重试。

while (true)
{
    listener.Start();
    Console.WriteLine("Listening...");
    HttpListenerContext context = listener.GetContext();
    HttpListenerRequest request = context.Request;

    HttpListenerResponse response = context.Response;
    response.StatusCode = (int)HttpStatusCode.InternalServerError;

    //Thread.Sleep(1000 * 1);
    string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
    byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
    response.ContentLength64 = buffer.Length;
    System.IO.Stream output = response.OutputStream;
    output.Write(buffer, 0, buffer.Length);
    output.Close();
    listener.Stop();
}
Run Code Online (Sandbox Code Playgroud)

使用上述代码,一切正常,并且分别在等待 3、9、27 和 81 秒后重试。

但是,如果我取消注释该Thread.Sleep调用,客户端会重试一次,然后挂起,直到调用超时并进行其他 3 次重试,这不是正确的行为。

同样的事情也发生在实际的生产 API 中,这让我相信这不是我的测试 API 的问题。

Ste*_*ary 5

在内部 使用 PollyHttpClient效果不太好。Single 意SendAsync为单次调用。IE:

  • 任何HttpClient超时都适用于单个SendAsync调用。
  • 某些版本还会HttpClient处置其内容,因此无法在下次SendAsync调用中重用。
  • 正如评论中所述,这种挂起是一个已知问题,Polly 无法修复。

底线:覆盖SendAsync对于添加请求前和请求后逻辑非常有用。这不是重试的正确位置。

相反,使用常规HttpClient并让 Polly 逻辑在(或其他)调用之外GetStringAsync试。