HttpClientFactory 结合 Polly 轻松实现重试机制

  • A+
所属分类:.NET技术
摘要

我们的服务里有一个 API 会去调用第三方的接口,设置了超时时间,最近偶尔会发生超时的情况,微软在提供 HttpClientFactory 的同时,也提供了一个基于 Polly 的一个扩展,我们可以借助它轻松地实现重试,熔断等行为。


HttpClientFactory 结合 Polly 轻松实现重试机制

Intro

我们的服务里有一个 API 会去调用第三方的接口,设置了超时时间,最近偶尔会发生超时的情况,微软在提供 HttpClientFactory 的同时,也提供了一个基于 Polly 的一个扩展,我们可以借助它轻松地实现重试,熔断等行为。

Sample

使用 Polly 扩展需要引用 nuget 包 :Microsoft.Extensions.Http.Polly

使用示例:

services.AddHttpClient(ServiceConst.IterableHttpClientName, x => {     x.Timeout = new TimeSpan(0, 0, 3); })     .AddTransientHttpErrorPolicy(builder =>     {         return builder.Or<TaskCanceledException>()             .Or<OperationCanceledException>()             .Or<TimeoutException>()             .OrResult(res => res.StatusCode == HttpStatusCode.TooManyRequests || (int)res.StatusCode >= 500)             .RetryAsync(5)             ;     }) 

通过 AddTransientHttpErrorPolicy 扩展方法来注册一个 Polly 的 policy,具体可以通过 policyBuilder 委托来定制自己要处理的情况和 policy 行为,支持方式有很多可以简单的指定重试,也可以指定 WaitANdRetryAsync 等待一段时间后重试,可以重试一次也可以一直重试下去,非常的灵活,可以根据自己的业务场景进行定制化配置,这里的示例直接是用了简单的重试机制

单元测试

下面提供了一个测试重试的单元测试,也可以作为使用示例的一个参考:

 [Fact] public async Task TaskCanceledException() {     var ticks = new ConcurrentBag<long>();     var retryLimit = 5;      var services = new ServiceCollection();     services.AddHttpClient("test", x =>         {             x.Timeout = TimeSpan.FromSeconds(1);         })         .AddTransientHttpErrorPolicy(builder =>         {             return builder.Or<TaskCanceledException>()                     .Or<OperationCanceledException>()                     .Or<TimeoutException>()                     .OrResult(res =>                         res.StatusCode == HttpStatusCode.TooManyRequests || (int)res.StatusCode >= 500)                     .RetryAsync(retryLimit)                 ;         })         .AddHttpMessageHandler(() => new MockHttpHandler(request =>         {             ticks.Add(DateTime.UtcNow.Ticks);             throw new TaskCanceledException();         }))          ;     await using var provider = services.BuildServiceProvider();     try     {         using var response = await provider.GetRequiredService<IHttpClientFactory>()             .CreateClient("test")             .GetAsync("api/test");     }     catch (Exception e)     {         Assert.True(e is OperationCanceledException);     }     Assert.Equal(retryLimit + 1, ticks.Count); }  private class MockHttpHandler : DelegatingHandler {     private readonly Func<HttpRequestMessage, HttpResponseMessage> _getResponseFunc;      public MockHttpHandler(Func<HttpRequestMessage, HttpResponseMessage> getResponseFunc)     {         _getResponseFunc = getResponseFunc;     }      protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)     {         return Task.FromResult(_getResponseFunc(request));     } } 

More

除了 AddTransientHttpErrorPolicy 之外,Polly 扩展还支持 AddPolicyHandler/AddPolicyHandlerFromRegistry 扩展方法,有兴趣的可以自己探索一下哈~~

Reference