문제 설명
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)));
이 클래스는 외부 서비스를 호출하지만 재시도하지 않는 클래스에 해당합니다. 실패시. 폴리를 설치했습니다. 오류가 발생하지 않습니다. 단순히 실패하면 재시도하지 않는 것입니다. 제가 잘못 알고 있는 건가요?
아래는 CustomClientFactory
에 대한 부분 코드입니다.
public class CustomClientFactory: ICustomClientFactory
{
private static readonly HttpClient _httpClient;
private IConfiguration _config;
public CustomClientFactory(IConfiguration config)
{
_config = config;
}
// other methods
}
참조 솔루션
방법 1:
First, the AddTransientHttpErrorPolicy has been preconfigured errors to handle errors in the following categories: Network failures, http 5XX and Http 408 status code.
And since this Policy was configured to the CustomClientFactory, only the http request which is sent from the CustomClientFactory will use this policy, it means that if you directly using Postman to access the external service, without via the CustomClientFactory, it will not trigger this policy.
You can refer the following sample:
Prerequisites: Install the Microsoft.Extensions.Http.Polly package
Create a WeatherForecastController controller: In this controller, since there have two get method with the same route, it will show the 500 error.
[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" };
}
}
Then Create a TestClientFactory with the following code:
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");
}
}
And configure the retry policy in the ConfigureService method:
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));
//}
Then, create a Test API controller to use the 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());
}
}
After running the application, we can see that: When using the postman directly access the WeatherForecastController, the result as below:
When using the postman access the WeatherForecastController via the CustomClientFactory, the result like this:
We can see that the retry policy works and it try multiple times.
(by Riko Hamblin、Zhi Lv)