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)
            .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 擴展方法,有興趣的可以自己探索一下哈~~

Reference

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章