[英]HttpClient Polly WaitAndRetry policy
任何人都知道为什么下面的政策在 3 次而不是 10 次后停止重试?
IAsyncPolicy<HttpResponseMessage> httpWaitAndRetryPolicy =
Policy.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.OrHandle<Exception>(r => true)
.WaitAndRetryAsync(10, retryAttempt => TimeSpan.FromSeconds(2));
我将重试尝试设置为 10 并测试 http 后调用,但 BadRequest 失败。 但它只重试 3 次然后停止直到超时并抛出异常
----> System.Threading.Tasks.TaskCanceledException : A task was canceled.
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at HttpRetry.Lab.Tests.ServiceTest.WhenPostWrongAlert_ThenRecoversProperly() in C:\ServiceTest.cs:line 56
--TaskCanceledException
15:57:03.6367 INFO HttpClientProvider - Configuring client xxxxxxxx:1234/api/" timeout=00:02:00
15:57:03.6636 INFO Service - POST xxxx/xxxxxxx
15:57:04.2051 INFO HttpClientProvider - Retrying retryCount=1 sleepDuration=00:00:02 result=Polly.DelegateResult`1[System.Net.Http.HttpResponseMessage]
15:57:06.6880 INFO HttpClientProvider - Retrying retryCount=2 sleepDuration=00:00:02 result=Polly.DelegateResult`1[System.Net.Http.HttpResponseMessage]
15:59:03.6811 INFO HttpClientProvider - Retrying retryCount=3 sleepDuration=00:00:02 result=Polly.DelegateResult`1[System.Net.Http.HttpResponseMessage]
15:59:03.6811 ERROR ServiceTest - System.Threading.Tasks.TaskCanceledException: A task was canceled.
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at HttpRetry.Lab.Service.<PostAsync>d__4.MoveNext() in C:\Service.cs:line 38
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at HttpRetry.Lab.Tests.ServiceTest.<PostAsync>d__4.MoveNext() in C:\ServiceTest.cs:line 27
var serviceProvider = serviceConnection.AddHttpClient(connection.Name, c =>
{
c.BaseAddress = new Uri(connection.BaseUrl);
c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{connection.UserName}:{connection.Password}")));
c.Timeout = connection.Timeout; // Timeout is TimeSpan.FromSeconds(120)
})
.AddPolicyHandler(httpWaitAndRetryPolicy)
.Services.BuildServiceProvider();
HttpClientFactories.Add(connection.Name, serviceProvider.GetService<IHttpClientFactory>());
确认问题的根本原因:我不知道是什么导致了症状,但看起来请求连接不会被释放,除非显式调用 Dispose HttpResponseMessage OnRetry。 当前的解决方案是在 WaitAndRetryAsync 中设置 OnRetry 并处理响应。 一切正常,无需更改 ServicePointManager.DefaultConnectionLimit
正如我所看到的,您在 HttpClient 级别上有 1 分钟的全局超时。 即使您可能期望TimeoutException
,这也会引发TaskCanceledException
。
如果您希望接收TimeoutException
,则必须通过HttpRequestMessage
的RequestTimeout
属性指定基于请求的超时。 有关详细信息,请查看以下链接。
您的重试逻辑定义了 3 次(或 10 次)重试,惩罚为 5 秒。 3 次重试意味着 4 次尝试,因为有一个初始(第 0 个)请求,它在重试 scope 之外。 如果失败,那么第一次重试将成为第二次尝试。
所以流程将如下所示:
如果所有这些都可以在一秒钟内完成,那么由于全局超时,HttpClient 将抛出一个TaskCanceledExpcetion
。
Polly 也支持本地和全局超时策略。 有一个单一的超时策略,可以以两种方式使用。
如果您的超时策略包含在重试中,它可以充当本地超时: retryPolicy.WrapAsync(timeoutPolicy);
如果您的重试策略包含在超时内,它可以充当全局超时: timeoutPolicy.WrapAsync(retryPolicy);
当然,您也可以同时拥有全局和本地超时: Policy.WrapAsync(globalTimeoutPolicy, retryPolicy, localTimeoutPolicy);
我强烈建议您考虑使用 Polly 的 Timeout 而不是 HttpClient 的 Timeout,以便在一个地方定义您的弹性策略。
请记住,如果超时没有响应,超时策略将抛出TimeoutRejectedException
。 因为您的重试处理各种异常( .OrHandle<Exception>()
),所以您不需要修改重试策略。
有一个名为Microsoft.Extensions.Http.Polly (1 ) 的 nuget package,它定义了几个有用的实用程序。 其中之一是HttpPolicyExtensions.HandleTransientHttpError()
它捕获HttpRequestException
并检查响应的状态代码是5xx还是408 (RequestTimeout)。
也可能值得考虑使用它。
每个不同的策略都定义了回调,以提供了解它们如何工作的能力。 在重试的情况下,分别调用onRetry
或onRetryAsync
进行同步或异步重试。 通过在WaitAndRetryAsync
中提供以下委托,您可以获得非常有用的信息:
onRetryAsync: (exception, delay, times, context) => {
//TODO: logging
}
结果,我们的团队发现 Polly Retry 策略在使用 HttpResponseMessage 之前不会释放 http 请求连接。 确切地说,这与 Polly Retry 无关,只是连接不会被释放,直到原始 HttpClient.SendAsync 得到返回。 并且由于 WaitAndRetry 而发生的重试策略有点延迟。 结束 Polly 重试策略(例如 x 次)可能最终会使用每个 BadRequest 的 x+1 个并发 http 连接。
有两种“使用”HttpResponseMessage 的方法。 通过显式调用 response.Result.Dispose 或对响应内容执行某种读取。 例如 response.Result.ReadAsAsync。 好吧,另一种方法是等待 httpClient 超时,但我相信这并不是我们真正想要的。 这是使事情正常进行的代码。 关键是 OnRetry 中的 HttpResponse.Dispose
ServicePointManager.DefaultConnectionLimit = appConfiguration.HttpClients.ConnectionLimit;
IAsyncPolicy<HttpResponseMessage> httpWaitAndRetryPolicy =
Policy.Handle<HttpRequestException>()
.Or<Exception>()
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(3,
retryAttempt => TimeSpan.FromSeconds(appConfiguration.HttpClients.RetryFactor * retryAttempt),
OnRetry);
IAsyncPolicy<HttpResponseMessage> timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(appConfiguration.HttpClients.RequestTimeout);
foreach (var endpoint in appConfiguration.HttpClients.HttpEndpoints)
{
var serviceProvider = serviceConnection.AddHttpClient(endpoint.Name,
c =>
{
c.BaseAddress = new Uri(endpoint.BaseUrl);
c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(endpoint.AuthenticationScheme, Convert.ToBase64String(Encoding.ASCII.GetBytes($"{endpoint.UserName}:{endpoint.Password}")));
c.DefaultRequestHeaders.ConnectionClose = false;
c.Timeout = endpoint.Timeout;
}).AddPolicyHandler(Policy.WrapAsync(httpWaitAndRetryPolicy, timeoutPolicy))
.Services.BuildServiceProvider();
httpClientFactories.Add(endpoint.Name, serviceProvider.GetService<IHttpClientFactory>());
}
private Task OnRetry(DelegateResult<HttpResponseMessage> response, TimeSpan span, int retryCount, Context context)
{
if (response == null)
return Task.CompletedTask;
var responseResult = response.Result;
logger.Info($"RetryCount={retryCount} IsSuccess={responseResult == null ? "" : responseResult.IsSuccessStatusCode} StatusCode={responseResult == null ? "" : responseResult.StatusCode} Exception={response.Exception?.Message});
response.Result?.Dispose();
return Task.CompletedTask;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.