我现在有什么?
目前,我有一个配置了RetryAsync
策略的客户端,该策略使用主地址并在发生故障时切换到故障转移地址。连接详细信息从机密管理器中读取。
services
.AddHttpClient ("MyClient", client => client.BaseAddress = PlaceholderUri)
.ConfigureHttpMessageHandlerBuilder (builder => {
// loads settings from secret manager
var settings = configLoader.LoadSettings().Result;
builder.PrimaryHandler = new HttpClientHandler {
Credentials = new NetworkCredential (settings.Username, settings.Password),
AutomaticDecompression = DecompressionMethods.GZip
};
var primaryBaseAddress = new Uri (settings.Host);
var failoverBaseAddress = new Uri (settings.DrHost);
builder.AdditionalHandlers.Add (new PolicyHttpMessageHandler (requestMessage => {
var relativeAddress = PlaceholderUri.MakeRelativeUri (requestMessage.RequestUri);
requestMessage.RequestUri = new Uri (primaryBaseAddress, relativeAddress);
return HttpPolicyExtensions.HandleTransientHttpError ()
.RetryAsync ((result, retryCount) =>
requestMessage.RequestUri = new Uri (failoverBaseAddress, relativeAddress));
}));
});
我想达到什么目标?
一般来说
我的客户可以使用主服务或故障转移服务。当主服务器关闭时,使用故障转移直到主服务器备份。当两者都关闭时,我们会收到警报,并且可以通过秘密管理器动态更改服务地址。
在代码中
现在我还想介绍一个CircuitBreakerPolicy
并将这两个策略链接在一起。我正在寻找一种封装的配置,并且在客户端级别而不是在使用该客户端的类上处理故障。
场景解释
让我们假设有一个断路器策略包装在具有单个客户端的重试策略中。
断路器配置为在主基地址上的瞬态错误尝试失败 3次后断开电路60 秒。- 地址从主地址更改为故障转移。OnBreak
重试策略配置为处理BrokenCircuitException
,并重试一次,并将地址从主更改为故障转移以继续。
- 主地址请求 - 500 代码
- 主地址请求 - 500 代码
- 主地址请求 - 500 代码(连续 3 次失败)
- 断路60秒
- 对主地址的请求 -
BrokenCircuitException
被重试策略捕获,调用故障转移 - 对主地址的请求 -
BrokenCircuitException
被重试策略捕获,调用故障转移 - 对主地址的请求 -
BrokenCircuitException
被重试策略捕获,调用故障转移 - 对主地址的请求 -
BrokenCircuitException
被重试策略捕获,调用故障转移 - (60 秒后)电路半开 - (此处可以再断开 60 秒或打开 - 假设打开)
- 主地址请求 - 200 代码
如本文所述,有一个解决方案是使用包装在回退中的断路器,但正如您在那里看到的,默认和回退的逻辑是在类中实现的,而不是在客户端级别上实现的。
我想
public class OpenExchangeRatesClient
{
private readonly HttpClient _client;
private readonly Policy _policy;
public OpenExchangeRatesClient(string apiUrl)
{
_client = new HttpClient
{
BaseAddress = new Uri(apiUrl),
};
var circuitBreaker = Policy
.Handle<Exception>()
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: 2,
durationOfBreak: TimeSpan.FromMinutes(1)
);
_policy = Policy
.Handle<Exception>()
.FallbackAsync(() => GetFallbackRates())
.Wrap(circuitBreaker);
}
public Task<ExchangeRates> GetLatestRates()
{
return _policy
.ExecuteAsync(() => CallRatesApi());
}
public Task<ExchangeRates> CallRatesApi()
{
//call the API, parse the results
}
public Task<ExchangeRates> GetFallbackRates()
{
// load the rates from the embedded file and parse them
}
}
改写为
public class OpenExchangeRatesClient
{
private readonly HttpClient _client;
public OpenExchangeRatesClient (IHttpClientFactory clientFactory) {
_client = clientFactory.CreateClient ("MyClient");
}
public Task<ExchangeRates> GetLatestRates () {
return _client.GetAsync ("/rates-gbp-usd");
}
}
我读了什么?
我尝试了什么?
我尝试了几种不同的场景来链接断路器策略并将其与重试策略相结合,以在启动文件中的客户端杠杆上实现预期目标。最后一个状态如下。这些策略按照重试能够捕获 a 的顺序进行包装BrokenCircuitException
,但情况并非如此。在消费者类上抛出异常,这不是预期的结果。虽然RetryPolicy
被触发了,但是还是抛出了消费者类上的异常。
var retryPolicy = GetRetryPolicy();
var circuitBreaker = GetCircuitBreakerPolicy();
var policyWraper = Policy.WrapAsync(retryPolicy, circuitBreaker);
services
.AddHttpClient("TestClient", client => client.BaseAddress = GetPrimaryUri())
.AddPolicyHandler(policyWraper);
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(
3,
TimeSpan.FromSeconds(45),
OnBreak,
OnReset,
OnHalfOpen);
}
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return Policy<HttpResponseMessage>
.Handle<Exception>()
.RetryAsync(1, (response, retryCount) =>
{
Debug.WriteLine("Retries on broken circuit");
});
}
我省略了方法,OnBreak
因为它们只是打印一些消息。OnReset
OnHalfOpen
更新:从控制台添加了日志。
Circuit broken (after 3 attempts)
Retries on broken
Exception thrown: 'System.AggregateException' in System.Private.CoreLib.dll
Retries on broken circuit
Exception thrown: 'System.AggregateException' in System.Private.CoreLib.dll
'CircuitBreakerPolicy.exe'(CoreCLR:clrhost):加载'C:\ Program Retries on broken circuit 异常抛出:System.Private.CoreLib.dll中的'System.AggregateException'
更新 2:使用配置了策略的客户端向类添加了参考 URL
更新 3:该项目已更新,以便WeatherService2.Get
以所需的方式实施工作:当主要服务不可用时,电路中断,使用故障转移服务直到电路关闭。这将是这个问题的答案,但是我想探索一个解决方案,WeatherService.Get
在Startup
.
在上面的日志中可以看到Exception thrown: 'System.AggregateException' in System.Private.CoreLib.dll
断路器抛出了哪些 - 这是不期望的,因为有重试包装断路器。