【发布时间】:2018-12-31 11:41:18
【问题描述】:
我在结合HttpClient 设置 Polly 的 CircuitBreaker 时遇到问题。
具体来说,CircuitBreaker 和 HttpClient 用于 ASP.NET Core Web API 控制器,链接如下:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests
https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory
下面是我想要的
重试策略:如果出现瞬时错误,每个请求重试3次。
cicuit Breaker 策略:如果所有请求都发生五个瞬时错误,则生效。
问题
虽然重试策略正常工作,但断路器策略不起作用。
在 _httpClient.SendAsync() 发生 5 次异常后,CarController 仍会收到请求,并且不会暂停 30 秒(控制器会立即处理请求)。
HandledEventsAllowedBeforeBreaking:5
DurationOfBreakInSeconds:30
我错过了什么吗?
配置服务
配置 Polly 重试和断路器策略,并指定自定义 HttpClient,HttpClientService
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
services.AddHttpClient<IHttpClientService, HttpClientService>()
.AddPolicyHandler((service, request) =>
HttpPolicyExtensions.HandleTransientHttpError()
.WaitAndRetryAsync(3,
retryCount => TimeSpan.FromSeconds(Math.Pow(2, retryCount)),
onRetry: (outcome, timespan, retryCount, context) =>
{
service.GetService<ILog>().Error("Delaying for {delay}ms, then making retry {retry}.",
timespan.TotalMilliseconds, retryCount);
}
)
)
)
.AddPolicyHandler((service, request) =>
HttpPolicyExtensions.HandleTransientHttpError()
//Further external requests are blocked for 30 seconds if five failed attempts occur sequentially.
//Circuit breaker policies are stateful.All calls through this client share the same circuit state.
.CircuitBreakerAsync(5,
TimeSpan.FromSeconds(30),
(result, timeSpan, context)=>
service.GetService<ILog>().Error("CircuitBreaker onBreak for {delay}ms", timeSpan.TotalMilliseconds),
context =>
service.GetService<ILog>().Error("CircuitBreaker onReset")));
}
汽车控制器
IHttpClientService 在 ConfigureServices 的 Polly 策略中指定。 HttpClientService 使用 HttpClient。
断路器不工作:即使发生来自_httpClient.SendAsync() 的五个瞬态错误(例如HttpRequestException),CarController 仍然可以接收请求,并且不会暂停 30 秒。
[ApiVersion("1")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class CarController : ControllerBase
{
private readonly ILog _logger;
private readonly IHttpClientService _httpClientService;
private readonly IOptions<Config> _config;
public CarController(ILog logger, IHttpClientService httpClientService, IOptions<Config> config)
{
_logger = logger;
_httpClientService = httpClientService;
_config = config;
}
[HttpPost]
public async Task<ActionResult> Post()
{
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
string body = reader.ReadToEnd();
var statusCode = await _httpClientService.PostAsync(
"url",
new Dictionary<string, string>
{
{"headerID", "Id"}
},
body);
return StatusCode((int)statusCode);
}
}
}
HttpClientService
似乎 HttpClient 在请求之间没有状态。
断路器不工作:即使发生来自_httpClient.SendAsync() 的五个瞬态错误(例如HttpRequestException),CarController 仍然可以接收请求,并且不会暂停 30 秒。
public class HttpClientService
{
private readonly HttpClient _httpClient;
public HttpClientService(HttpClient client)
{
_httpClient = client;
}
public async Task<HttpStatusCode> PostAsync(string url, Dictionary<string, string> headers, string body)
{
using (var content = new StringContent(body, Encoding.UTF8, "application/json"))
{
foreach (var keyValue in headers)
{
content.Headers.Add(keyValue.Key, keyValue.Value);
}
var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = content
};
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
return response.StatusCode;
}
}
ASP.NET Core API 2.2
更新 更新了 SetWaitAndRetryPolicy 扩展方法以使用 IServiceProvider。
【问题讨论】:
-
我猜问题是因为你调用了两次
AddHttpClient,但你没有与它共享熔断策略(也不是WaitAndRetryAsync)。我不太确定您创建IHttpClientService.. 的方式。您不应该在注册时为其设置基地址吗? -
另请注意:当您达到异常限制(在您的情况下为 3)时,状态机将移动到
Open状态。不过,这不会阻止您的控制器被调用。控制器本身可以被调用,但Http调用将快速失败,BrokenCircuitException将保持这种状态 30 秒。 30 秒后,电路将移动到HalfOpen,将第一个呼叫作为试验,并决定它是回到Open还是恢复正常Closed。看看这里github.com/App-vNext/Polly/wiki/…
标签: c# asp.net-core .net-core asp.net-core-webapi polly