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时,结果如下:
我们可以看到重试策略有效并且尝试了多次。
在我的 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时,结果如下:
我们可以看到重试策略有效并且尝试了多次。