HttpClientFactory 結合 Polly 輕松實現重試機制
Intro
我們的服務里有一個 API 會去調用第三方的接口,設置了超時時間,最近偶爾會發生超時的情況,微軟在提供 HttpClientFactory 的同時,也提供了一個基於 Polly 的一個擴展,我們可以借助它輕松地實現重試,熔斷等行為。
Sample
使用 Polly 擴展需要引用 nuget 包 :Microsoft.Extensions.Http.Polly
使用示例:
services.AddHttpClient("test", x =>
{
x.Timeout = new TimeSpan(0, 0, 3);
})
.AddTransientHttpErrorPolicy(builder =>
{
return builder.Or<TaskCanceledException>()
.Or<OperationCanceledException>()
.Or<TimeoutException>()
.OrResult(res => res.StatusCode == HttpStatusCode.TooManyRequests)
.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)
.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 擴展方法,有興趣的可以自己探索一下哈~~
