HttpClient Polly WaitAndRetry 策略 [英] HttpClient Polly WaitAndRetry policy

查看:152
本文介绍了HttpClient Polly WaitAndRetry 策略的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人知道为什么下面的策略在 3 次而不是 10 次后停止重试吗?

Anyone have any idea on why the policy below stop retry after 3 times instead of 10 ?

IAsyncPolicy<HttpResponseMessage> httpWaitAndRetryPolicy =
     Policy.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
          .OrHandle<Exception>(r => true)
          .WaitAndRetryAsync(10, retryAttempt => TimeSpan.FromSeconds(2));

我将重试尝试设置为 10 并在 BadRequest 失败的情况下测试 http post 调用.但它只重试3次然后停止直到超时并抛出异常

I set the retry attempt to 10 and test the http post call with BadRequest failure. But it only retry 3 times and then stopped until timeout and threw exception

  ----> 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 并处理 resposne.一切正常,无需更改 ServicePointManager.DefaultConnectionLimit

Confirmed the root cause of the issue: I don't know what causes the symptom but looks like the request connection will not be released unless explicitly calling Dispose HttpResponseMessage OnRetry. The current solution is setup OnRetry in WaitAndRetryAsync and dispose the resposne. Everything's working fine without the needs of changing ServicePointManager.DefaultConnectionLimit

推荐答案

您的超时

正如我所见,您在 HttpClient 级别有一个全局 1 分钟超时.即使您可能期望 TimeoutException,这也会抛出 TaskCanceledException.

如果您希望接收TimeoutException,那么您必须通过HttpRequestMessageRequestTimeout 属性指定基于请求的超时.有关更多详细信息,请查看以下链接.

If you wish to receive TimeoutException then you have to specify a request based timeout via the RequestTimeout property of HttpRequestMessage. For further details please check the following link.

您的重试逻辑定义了 3(或 10)次重试,罚时为 5 秒.3 次重试意味着 4 次尝试,因为有一个初始(第 0 个)请求,它在重试范围之外.如果失败,则第一次重试将成为第二次尝试.

Your retry logic defines 3 (or 10) retries with 5 seconds penalty. 3 retries means 4 attempts, because there is an initial (0th) request, which is out-side of the retry scope. If that fails then the 1st retry will become the 2nd attempt.

所以流程看起来像这样:

So the flow will look like this:

  1. 发出初始请求<<第一次尝试
  2. 初始请求失败
  3. 触发重试逻辑
  4. 5 秒惩罚被暴露
  5. 第一个重试逻辑触发
  6. 第二次尝试
  7. 第二次尝试失败
  8. 触发重试逻辑
  9. 5 秒惩罚被暴露
  10. 第二次重试逻辑触发
  11. 第三次尝试
  12. ...

如果所有这些都可以在一秒钟内完成,那么 HttpClient 将由于全局超时而抛出 TaskCanceledExpcetion.

If all of this can finished under a second then HttpClient will throw an TaskCanceledExpcetion due to global timeout.

Polly 也支持本地和全局超时策略.有一个超时政策,可以以两种方式使用.

Polly supports local and global timeout policies as well. There is a single Timeout policy, which can be used in both ways.

如果您的超时策略包含在重试中,它可以充当本地超时:retryPolicy.WrapAsync(timeoutPolicy);

It can act as a local timeout if your timeout policy is wrapped inside the the retry: retryPolicy.WrapAsync(timeoutPolicy);

如果您的重试策略包含在超时内,它可以充当全局超时:timeoutPolicy.WrapAsync(retryPolicy);

It can act as a global timeout if your retry policy is wrapped inside the timeout: timeoutPolicy.WrapAsync(retryPolicy);

当然,您也可以同时拥有全局和本地超时:Policy.WrapAsync(globalTimeoutPolicy, retryPolicy, localTimeoutPolicy);

Of course, you can have global and local timeouts as well at the same time: Policy.WrapAsync(globalTimeoutPolicy, retryPolicy, localTimeoutPolicy);

我强烈建议您考虑使用 Polly 的超时而不是 HttpClient 的超时,以便在一个地方定义您的弹性策略.

I would highly encourage you to consider to use Polly's Timeout instead of HttpClient's Timeout in order to have a single place where you define your resilient strategy.

请记住,如果超时没有响应,超时策略将抛出 TimeoutRejectedException.因为您的重试处理所有类型的异常 (.OrHandle()),所以您不需要修改您的重试策略.

Please bear in mind that Timeout policy will throw TimeoutRejectedException if the timeout elapsed without a response. Because your retry handles all sort of Exceptions (.OrHandle<Exception>()), that why you don't need to modify your retry policy.

有一个名为 Microsoft.Extensions.Http.Polly (1),它定义了几个有用的实用程序.其中之一是 HttpPolicyExtensions.HandleTransientHttpError()

There is a nuget package called Microsoft.Extensions.Http.Polly (1), which defines several useful utilities. One of them is the HttpPolicyExtensions.HandleTransientHttpError()

它捕获 HttpRequestException 并检查响应的状态代码是 5xx 还是 408 (RequestTimeout).

It catches HttpRequestException and checks whether the response's status code is either 5xx or 408 (RequestTimeout).

也可以考虑使用它.

每个不同的策略都定义了回调,以提供深入了解其工作方式的能力.在重试的情况下,分别称为 onRetryonRetryAsync 用于同步或异步重试.通过在 WaitAndRetryAsync 中提供以下委托,您可以获得非常有用的信息:

Each of different policies define callback to provide ability to get insight how they work. In case of retry it is called onRetry or onRetryAsync for sync or async retry respectively. You by providing the following delegate inside your WaitAndRetryAsync you can get really useful information:

onRetryAsync: (exception, delay, times, context) => {
  //TODO: logging
}

这篇关于HttpClient Polly WaitAndRetry 策略的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆