首页 > 解决方案 > 基于请求条件的断路器

问题描述

我需要设置断路器策略,以便它仅针对某些特定请求断开电路。

我有一种网关A调用 API B,后者又调用CD。我正在 A 中设置断路器策略。到达 A 的初始请求有一个参数,稍后用于决定是调用C还是D,比如说http://gateway.A.com?usageParam=C。我想以这样的方式配置断路器,该电路可以为CD单独打开。我的意思是,如果D失败,调用 withusageParam=D应该立即失败,但usageParam=C应该仍然可以正常进行,反之亦然。

示例服务依赖项

标签: asp.net-coreasp.net-core-3.1pollycircuit-breaker

解决方案


简单地说,断路器只能有一个状态。其中之一:Closed, Open, Half-Open.

您应该将 CB 视为代理。如果下游系统被认为是健康的,它允许每个请求通过它。如果 CB 检测到瞬时故障(通过检查响应(如果有)),那么它将通过拒绝所有请求来缩短执行。


因此,如果您想区分下游系统的健康状况CD那么您需要两个 CB(每个一个)。这允许您分别允许或拒绝针对不同子系统的传出请求。

选项 A

您可以将两个 CB 放在 serviceA中。在这里,您可以注册两个命名的 HttpClient,它们装饰有不同的断路器:

private IAsyncPolicy<HttpResponseMessage> GetCBPolicy()
    => HttpPolicyExtensions
        .HandleTransientHttpError()
        .CircuitBreakerAsync(2, TimeSpan.FromSeconds(1));

...
services.AddHttpClient("CService", c => { 
            c.BaseAddress = new Uri(BServiceBasePath);
            ... 
         })
        .AddPolicyHandler(GetCBPolicy());
services.AddHttpClient("DService" c => { 
            c.BaseAddress = new Uri(BServiceBasePath);
            ... 
         })
        .AddPolicyHandler(GetCBPolicy());

但是您必须手动进行路由,如下所示:

if(!Context.Request.Query.TryGetValue("usageParam", out var usage))
   return;

HttpClient client = usage switch
{
    "C" => _clientFactory.CreateClient("CService"),
    "D" => _clientFactory.CreateClient("DService"),
    _ => throw new NotSupportedException("..."),
};

选项 B

您可以将两个 CB 放在 serviceB中。在这里,您可以注册两个类型化的 HttpClient,它们装饰有不同的断路器:

private IAsyncPolicy<HttpResponseMessage> GetCBPolicy()
    => HttpPolicyExtensions
        .HandleTransientHttpError()
        .CircuitBreakerAsync(2, TimeSpan.FromSeconds(1));

...
services.AddHttpClient<ICService, CService>()
        .AddPolicyHandler(GetCBPolicy());
services.AddHttpClient<IDService, DService>()
        .AddPolicyHandler(GetCBPolicy());    

推荐阅读