代码之家  ›  专栏  ›  技术社区  ›  Pingpong

polly断路器策略与基于asp.net核心api的httpclient

  •  0
  • Pingpong  · 技术社区  · 6 年前

    我在设置波利的断路器时遇到问题 HttpClient .

    明确地, CircuitBreaker HTTP客户端 用于以下链接的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次。

    • 电路断路器策略:如果所有请求中发生五个瞬时错误,则生效。

    问题

    尽管重试策略工作正常,但断路器策略不工作。

    在发生来自HttpClient.SendAsync()的5个异常之后,Carcontroller仍然接收请求,并且在30秒内没有暂停(请求由控制器立即处理)。

    破裂前处理事件:5

    断裂秒持续时间: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 在中的polly策略中指定 ConfigureServices . HttpClientService服务 使用 HTTP客户端 .

    断路器不工作:即使发生五个瞬态错误(例如 HttpRequestException _httpClient.SendAsync() ,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在请求之间不是有状态的。

    断路器不工作:即使发生五个瞬态错误(例如 httprequestexception _ HttpClient.SendAsync() ,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核心API 2.2

    更新 已更新setwaitandretrypolicy扩展方法以使用iserviceprovider。

    1 回复  |  直到 6 年前
        1
  •  0
  •   mountain traveller    6 年前

    断路器策略是 stateful to track failure rates across calls ,所以必须 long-lived rather than created per request .

    所发布代码中httpclientfactory上的重载的使用方式:

    .AddPolicyHandler((service, request) => HttpPolicyExtensions.HandleTransientHttpError()
        .CircuitBreakerAsync( /* etc */
    

    manufacturing an instance of the circuit-breaker per request ,所以断路器永远没有时间建立故障状态。

    超载(和类似物)是 designed for selecting policies dynamically based on characteristics of the request . 但事实上,发布的代码(edit:the original posted code was)没有使用 service, request 输入参数,这样您就可以删除 (service, request) => 部分,并使用HttpClientFactory上的重载,该重载采用策略实例:

    .AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
        .WaitAndRetryAsync(/* etc */))
    .AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
        .CircuitBreakerAsync(/* etc */))
    

    HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(/* etc */) 然后将被httpclientfactory配置为使用它的httpclient实例使用。


    旁白 (特别是对于任何想使用具有给定过载的断路器的读者):

    可以使用请求驱动的断路器 policySelector HttpClientFactory中的重载。只需确保单个实例 挑选出来的 通过lambda表达式,并不是每次请求都生成一个新实例。例如:

    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.
    

    编辑以回答评论中的问题: 不必声明该实例 static 让它长寿。它可以在 Startup.ConfigureServices(...) 方法,在使用前立即执行,如上面的代码示例所示。lambda并在httpclientfactory上配置它将捕获它并使其长寿。

    这个 circuitBreaker 实例应该是 shared across calls you want to break in common . 如果将断路器连接到 HttpClient 通过httpclientfactory声明的配置,所有通过该实例的调用 HTTP客户端 DI稍后从HttpClientFactory检索到的配置将共享断路器,从而实现共同断开。

    在httpclientfactory中使用断路器时,这通常意味着您可以声明一个 HTTP客户端 每个子系统的HttpClientFactory上的配置(类型化或命名),您希望对其进行公共电路中断调用。


    司扥噢特: 还选择了断路器的变型 triggers based on consecutive fault count . (这只是为了以防万一;发布的问题是指请求之间发生的5个错误,但不是连续发生的。)