Polly WaitAndRetryAsync 在重试一次后挂起
Polly WaitAndRetryAsync hangs after one retry
如果 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>();
}
我有一个测试 API,它只创建一个 HttpListener
并在 while(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();
}
使用上面的代码一切正常,分别在等待 3 秒、9 秒、27 秒和 81 秒后重试。
但是,如果我取消对 Thread.Sleep
调用的注释,客户端会重试一次然后挂起,直到其他 3 次重试调用超时,这不是正确的行为。
同样的事情也发生在实际生产中API,这让我相信这不是我测试的问题API。
在 HttpClient
中使用 Polly 效果不是很好。单个 SendAsync
旨在成为单个调用。即:
- 任何
HttpClient
超时都将应用于单个 SendAsync
调用。
- 有些版本的
HttpClient
也处理了他们的内容,所以不能
在下一个 SendAsync
调用中重复使用。
- 如评论中所述,
这种挂起是 已知的
issue 不能
由 Polly 修复。
底线:覆盖 SendAsync
非常适合添加 pre-request 和 post-request 逻辑。这不是重试的正确位置。
相反,使用常规 HttpClient
并让您的 Polly 逻辑在 之外重试 GetStringAsync
(或其他)调用。
对于 .NET Framework 和在 HttpClient 中使用 Polly 的已知问题,这似乎是一个合适的解决方法。我们必须在重试时处理结果以允许多个请求。请参阅原始问题 here and another discussion describing the workaround here 上的讨论。我只是简单地测试了一下以确定它是否有效,但还没有完全研究 side-effects 可能存在的内容。
Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(msg => RetryableStatusCodesPredicate(msg.StatusCode))
.RetryAsync(retryCount, onRetry: (x, i) =>
{
x.Result.Dispose(); // workaround for https://github.com/aspnet/Extensions/issues/1700
}));
如果 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>();
}
我有一个测试 API,它只创建一个 HttpListener
并在 while(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();
}
使用上面的代码一切正常,分别在等待 3 秒、9 秒、27 秒和 81 秒后重试。
但是,如果我取消对 Thread.Sleep
调用的注释,客户端会重试一次然后挂起,直到其他 3 次重试调用超时,这不是正确的行为。
同样的事情也发生在实际生产中API,这让我相信这不是我测试的问题API。
在 HttpClient
中使用 Polly 效果不是很好。单个 SendAsync
旨在成为单个调用。即:
- 任何
HttpClient
超时都将应用于单个SendAsync
调用。 - 有些版本的
HttpClient
也处理了他们的内容,所以不能 在下一个SendAsync
调用中重复使用。 - 如评论中所述, 这种挂起是 已知的 issue 不能 由 Polly 修复。
底线:覆盖 SendAsync
非常适合添加 pre-request 和 post-request 逻辑。这不是重试的正确位置。
相反,使用常规 HttpClient
并让您的 Polly 逻辑在 之外重试 GetStringAsync
(或其他)调用。
对于 .NET Framework 和在 HttpClient 中使用 Polly 的已知问题,这似乎是一个合适的解决方法。我们必须在重试时处理结果以允许多个请求。请参阅原始问题 here and another discussion describing the workaround here 上的讨论。我只是简单地测试了一下以确定它是否有效,但还没有完全研究 side-effects 可能存在的内容。
Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(msg => RetryableStatusCodesPredicate(msg.StatusCode))
.RetryAsync(retryCount, onRetry: (x, i) =>
{
x.Result.Dispose(); // workaround for https://github.com/aspnet/Extensions/issues/1700
}));