HttpClient 重试逻辑未按预期工作(.Net Core 3.1)

HttpClient retry logic not working as expected (.Net Core 3.1)

在我的 Startup.cs 中,我有以下代码行:

services.AddHttpClient<CustomClientFactory>()
        .AddTransientHttpErrorPolicy(
            p => p.WaitAndRetryAsync(3, 
                retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 1)));

这对应于我的 class 调用外部服务,但它不会在失败时重试。我安装了 Polly。我没有收到任何错误,如果出现故障,它根本不会重试。我做错了吗?

下面是 CustomClientFactory:

的部分代码
public class CustomClientFactory: ICustomClientFactory
{

    private static readonly HttpClient _httpClient;

    private IConfiguration _config;

    public CustomClientFactory(IConfiguration config)
    {
        _config = config;
    }

   // other methods
}

首先,AddTransientHttpErrorPolicy 已预先配置错误以处理以下类别的错误:网络故障、http 5XX 和 Http 408 状态代码。

并且由于这个Policy配置到CustomClientFactory,只有从CustomClientFactory发出的http请求才会使用这个policy,这意味着如果你直接使用Postman访问外部服务,而不通过CustomClientFactory,它不会触发此政策。

您可以参考以下示例:

先决条件:安装 Microsoft.Extensions.Http.Polly 包

创建一个WeatherForecastController控制器:在这个控制器中,由于有两个相同路由的get方法,它会显示500错误。

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        var rng = new Random();
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
    [HttpGet]
    public IEnumerable<string> Get(int id)
    {
        return new string[] { "value1", "value2" };
    }
}

然后使用以下代码创建一个 TestClientFactory:

public class TestClientFactory
{
    private readonly HttpClient _httpClient;
    public TestClientFactory(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<List<WeatherForecast>> GetWeather()
    {
        _httpClient.BaseAddress = new Uri("https://localhost:5001"); 
        return await _httpClient.GetFromJsonAsync<List<WeatherForecast>>("WeatherForecast");
    }
}

并在ConfigureService方法中配置重试策略:

    public void ConfigureServices(IServiceCollection services)
    { 
        services.AddControllersWithViews();

        services.AddHttpClient<TestClientFactory>()
        .SetHandlerLifetime(TimeSpan.FromMinutes(5))
        .AddTransientHttpErrorPolicy(
        p => p.WaitAndRetryAsync(3,
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 1)));
        //you can also use the following method.
        //.AddPolicyHandler(GetRetryPolicy()); 
    }

    //private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    //{
    //    return HttpPolicyExtensions
    //    .HandleTransientHttpError()
    //    .WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(10));
    //}

然后,创建一个测试 API 控制器以使用 TestClientFactory:

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
    private readonly TestClientFactory _testClient;
    public TestController(TestClientFactory testClient)
    {
        _testClient = testClient;
    }

    [HttpGet]
    [Route("weather")]
    public async Task<IActionResult> GetWeatherAsync()
    {
        return Ok(await _testClient.GetWeather());
    }
}

应用运行后,我们可以看到: 使用postman直接访问WeatherForecastController时,结果如下:

使用postman通过CustomClientFactory访问WeatherForecastController时,结果如下:

我们可以看到重试策略有效并且尝试了多次。