[英]Polly Circuit Breaker policy and HttpClient with ASP.NET Core API
I have problems setting up Polly's CircuitBreaker in combination of HttpClient
. 我在结合HttpClient
设置Polly的CircuitBreaker时遇到问题。
Specifically, CircuitBreaker
and HttpClient
are used for ASP.NET Core Web API Controller following the links below: 具体来说,下面的链接将CircuitBreaker
和HttpClient
用于ASP.NET Core Web API Controller:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests
https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory
Below is what I want 以下是我想要的
retry policy: retry 3 times for each request if there are transient error. 重试策略:如果有暂时性错误,则每个请求重试3次。
cicuit breaker policy: take effect if fives transient errors occur across all requests. cicuit breaker策略:如果所有请求中发生5个瞬态错误,则该策略生效。
The problem 问题
Although retry policy works correctly, circuit breaker policy does not work. 尽管重试策略正确运行,但断路器策略不起作用。
The CarController still receives request after 5 exceptions from _httpClient.SendAsync() occurred and doesn't pause for 30 seconds (requests are processed immeidately by the controller). 在发生_httpClient.SendAsync()的5个异常后,CarController仍接收请求,并且不会暂停30秒(请求由控制器立即处理)。
HandledEventsAllowedBeforeBreaking: 5 打破之前的HandledEventsAllowedBefore:5
DurationOfBreakInSeconds: 30 DurationBreakInSeconds:30
Am I missing something here? 我在这里想念什么吗?
ConfigureServices ConfigureServices
Configure Polly retry and circuit breaker policies, and sepecify custom HttpClient, HttpClientService
配置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")));
}
CarController CarController
IHttpClientService
is specified in Polly policy in ConfigureServices
. IHttpClientService
在ConfigureServices
Polly策略中指定。 HttpClientService
uses HttpClient
. HttpClientService
使用HttpClient
。
Circuit breaker does not work: even after five transient error occurs (eg HttpRequestException
) from _httpClient.SendAsync()
, CarController can still receive request, and doesn't pause for 30 seconds. 断路器不起作用:即使在发生_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 HttpClientService
It seems HttpClient is not stateful across requests. 似乎HttpClient在请求中不是有状态的。
Circuit breaker does not work: even after five transient error occurs (eg HttpRequestException
) from _httpClient.SendAsync()
, CarController can still receive request, and doesn't pause for 30 seconds. 断路器不起作用:即使在发生_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 ASP.NET Core API 2.2
Update Updated SetWaitAndRetryPolicy extension method to use IServiceProvider. 更新更新的SetWaitAndRetryPolicy扩展方法以使用IServiceProvider。
Circuit-breaker policies are stateful to track failure rates across calls , and so need to be long-lived rather than created per request . 断路器策略是有状态的,可跟踪呼叫之间的故障率 ,因此需要长期存在,而不是根据请求创建 。
The way the overload on HttpClientFactory in the code posted is used: 在发布的代码中使用HttpClientFactory重载的方式:
.AddPolicyHandler((service, request) => HttpPolicyExtensions.HandleTransientHttpError()
.CircuitBreakerAsync( /* etc */
is manufacturing an instance of the circuit-breaker per request , so the circuit-breakers never have time to build up a failure state. 正在根据请求制造断路器的实例 ,因此断路器永远没有时间建立故障状态。
That overload (and similars) are designed for selecting policies dynamically based on characteristics of the request . 该重载(及类似内容) 旨在根据请求的特征动态选择策略 。 But in fact the posted code is (EDIT: the originally posted code was) not making any use of the service, request
input parameters, so you can just delete the (service, request) =>
part, and make use of an overload on HttpClientFactory which takes a policy instance: 但是实际上,发布的代码是(编辑:原始发布的代码是)不使用service, request
输入参数的,因此您可以删除(service, request) =>
部分,并利用重载带有策略实例的HttpClientFactory:
.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
.WaitAndRetryAsync(/* etc */))
.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
.CircuitBreakerAsync(/* etc */))
That single, long-lived instance of circuit-breaker returned by HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(/* etc */)
will then be used by the instances of HttpClient for which HttpClientFactory was configured to use it. HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(/* etc */)
返回的HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(/* etc */)
断路器的单个长期实例将由配置了HttpClientFactory的HttpClient实例使用。
Sidenote (especially for any readers who do want to use circuit-breaker with the given overloads): 旁注 (特别是对于那些想在给定过载下使用断路器的读者):
It is possible to use circuit-breaker with the request-driven policySelector
overloads in HttpClientFactory. 可以在HttpClientFactory policySelector
断路器与请求驱动的policySelector
重载一起使用。 It is just necessary to ensure that a single instance is selected by the lambda expression, not that a new instance is manufactured each time per request. 仅需要确保通过lambda表达式选择单个实例,而不是每次请求每次都制造一个新实例。 For example: 例如:
var circuitBreaker = HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(/* etc */);
services.AddHttpClient<IHttpClientService, HttpClientService>()
.AddPolicyHandler((service, request) => circuitBreaker); // By way of example technique: more typically with this overload, there is some more complex logic to select different policies for different kinds of request.
EDIT to answer question in comments: That instance does not have to be declared static
to make it long-lived. 编辑以在注释中回答问题:不必将该实例声明为static
即可使其具有长寿命。 It can be declared within the Startup.ConfigureServices(...)
method, immediately before use, as per the code example above. 根据上面的代码示例,可以在使用前立即在Startup.ConfigureServices(...)
方法中声明它。 The lambda and configuring it on HttpClientFactory will capture it and make it long-lived. lambda并在HttpClientFactory上配置它会捕获它并使它长寿。
The circuitBreaker
instance should be shared across calls you want to break in common . 该circuitBreaker
实例应在您想共同中断的呼叫之间共享 。 If you attach a circuit-breaker to a particular HttpClient
configuration declared via HttpClientFactory, all calls through instances of that HttpClient
configuration later retrieved from HttpClientFactory by DI, will share the circuitBreaker, and thus break in common. 如果将断路器附加到通过HttpClientFactory声明的特定HttpClient
配置中,则稍后通过DI从HttpClientFactory检索到的该HttpClient
配置实例的所有调用将共享circuitBreaker,从而共同中断。
When using circuit-breaker with HttpClientFactory, this typically means you might declare one HttpClient
configuration (typed or named) on HttpClientFactory per subsystem for which you want calls to circuit-break in common. 当将断路器与HttpClientFactory一起使用时,这通常意味着您可能希望在每个子系统上通过HttpClientFactory声明一个HttpClient
配置(键入或命名),要对该子系统进行公用调用。
Sidenote: The variant of circuit-breaker selected also triggers based on consecutive fault count . 旁注:所选断路器的变型也会基于连续的故障计数触发 。 (Mentioned just in case an extra factor; the question posted refers to 5 errors occurring across requests but not specifically consecutively.) (提及以防万一是一个额外的因素;发布的问题是指在请求中发生的5个错误,但不是连续的。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.