Die*_*mos 2 c# circuit-breaker .net-core polly asp.net-core
我编写了基于重试的弹性策略和熔断策略。现在可以工作,但其行为存在问题。
我注意到,当断路器打开half-open并且onBreak()事件再次执行以关闭电路时,会为重试策略触发一次额外的重试(这是状态的另一项the health verification)half-open。
让我一步步解释:
我定义了两个用于重试和断路器的强类型策略:
static Policy<HttpResponseMessage> customRetryPolicy;
static Policy<HttpResponseMessage> customCircuitBreakerPolicy;
static HttpStatusCode[] httpStatusesToProcess = new HttpStatusCode[]
{
HttpStatusCode.ServiceUnavailable, //503
HttpStatusCode.InternalServerError, //500
};
Run Code Online (Sandbox Code Playgroud)
重试策略的工作方式如下:每个请求两次 (2) 重试,每次重试之间等待五 (5) 秒。如果内部断路器打开,不得重试。仅重试 500 和 503 Http 状态。
customRetryPolicy = Policy<HttpResponseMessage>
//Not execute a retry if the circuit is open
.Handle<BrokenCircuitException>( x =>
{
return !(x is BrokenCircuitException);
})
//Stop if some inner exception match with BrokenCircuitException
.OrInner<AggregateException>(x =>
{
return !(x.InnerException is BrokenCircuitException);
})
//Retry if status are:
.OrResult(x => { return httpStatusesToProcess.Contains(x.StatusCode); })
// Retry request two times, wait 5 seconds between each retry
.WaitAndRetry( 2, retryAttempt => TimeSpan.FromSeconds(5),
(exception, timeSpan, retryCount, context) =>
{
System.Console.WriteLine("Retrying... " + retryCount);
}
);
Run Code Online (Sandbox Code Playgroud)
断路器策略的工作方式如下:允许最多连续三 (3) 次故障,然后断开电路三十 (30) 秒。仅适用于 HTTP-500 开路。
customCircuitBreakerPolicy = Policy<HttpResponseMessage>
// handling result or exception to execute onBreak delegate
.Handle<AggregateException>(x =>
{ return x.InnerException is HttpRequestException; })
// just break when server error will be InternalServerError
.OrResult(x => { return (int) x.StatusCode == 500; })
// Broken when fail 3 times in a row,
// and hold circuit open for 30 seconds
.CircuitBreaker(3, TimeSpan.FromSeconds(30),
onBreak: (lastResponse, breakDelay) =>{
System.Console.WriteLine("\n Circuit broken!");
},
onReset: () => {
System.Console.WriteLine("\n Circuit Reset!");
},
onHalfOpen: () => {
System.Console.WriteLine("\n Circuit is Half-Open");
});
Run Code Online (Sandbox Code Playgroud)
最后,这两个策略是这样嵌套的:
try
{
customRetryPolicy.Execute(() =>
customCircuitBreakerPolicy.Execute(() => {
//for testing purposes "api/values", is returning 500 all time
HttpResponseMessage msResponse
= GetHttpResponseAsync("api/values").Result;
// This just print messages on console, no pay attention
PrintHttpResponseAsync(msResponse);
return msResponse;
}));
}
catch (BrokenCircuitException e)
{
System.Console.WriteLine("CB Error: " + e.Message);
}
Run Code Online (Sandbox Code Playgroud)
我所期望的结果是什么?
看图片:
我正在尝试理解这种行为。为什么当断路器第二次、第三次……N次断开时还要执行一次额外的重试?
我已经检查了重试的机器状态模型和断路器策略,但我不明白为什么要执行此额外的重试。
断路器流程: https://github.com/App-vNext/Polly/wiki/Circuit-Breaker#putting-it-all-together-
重试策略流程: https://github.com/App-vNext/Polly/wiki/Retry#how-polly-retry-works
这确实很重要,因为正在等待重试的时间(本例为 5 秒),最终,这对于高并发来说是浪费时间。
任何帮助/指导,将不胜感激。非常感谢。
使用Polly.Context,您可以在两个策略之间交换信息(在您的情况下:重试和断路器)。上下文基本上是一个Dictionary<string, object>.
因此,技巧是在 上设置一个键,onBreak然后在sleepDurationProdiver.
让我们从内部断路器策略开始:
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return Policy<HttpResponseMessage>
.HandleResult(res => res.StatusCode == HttpStatusCode.InternalServerError)
.CircuitBreakerAsync(3, TimeSpan.FromSeconds(2),
onBreak: (dr, ts, ctx) => { ctx[SleepDurationKey] = ts; },
onReset: (ctx) => { ctx[SleepDurationKey] = null; });
}
Run Code Online (Sandbox Code Playgroud)
Open状态 2 秒,然后转换为HalfOpendurationOfBreak键值Closed状态 ( onReset) 时,它会删除该值现在,让我们继续重试策略:
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return Policy<HttpResponseMessage>
.HandleResult(res => res.StatusCode == HttpStatusCode.InternalServerError)
.Or<BrokenCircuitException>()
.WaitAndRetryAsync(4,
sleepDurationProvider: (c, ctx) =>
{
if (ctx.ContainsKey(SleepDurationKey))
return (TimeSpan)ctx[SleepDurationKey];
return TimeSpan.FromMilliseconds(200);
},
onRetry: (dr, ts, ctx) =>
{
Console.WriteLine($"Context: {(ctx.ContainsKey(SleepDurationKey) ? "Open" : "Closed")}");
Console.WriteLine($"Waits: {ts.TotalMilliseconds}");
});
}
Run Code Online (Sandbox Code Playgroud)
BrokenCircuitExceptionOpen),则它将在 200 毫秒内返回Open状态),则它将返回上下文中的值
onRetry它仅出于调试目的将一些值打印到内部控制台最后让我们连接策略并测试它
const string SleepDurationKey = "Broken";
static HttpClient client = new HttpClient();
static async Task Main()
{
var strategy = Policy.WrapAsync(GetRetryPolicy(), GetCircuitBreakerPolicy());
await strategy.ExecuteAsync(async () => await Get());
}
static Task<HttpResponseMessage> Get()
{
return client.GetAsync("https://httpstat.us/500");
}
Run Code Online (Sandbox Code Playgroud)
Get以异步方式调用该方法handledEventsAllowedBeforeBreaking是2输出
Context: Closed
Waits: 200
Context: Open
Waits: 2000
Context: Open
Waits: 2000
Context: Open
Waits: 2000
Run Code Online (Sandbox Code Playgroud)
handledEventsAllowedBeforeBreaking是 3输出
Context: Closed
Waits: 200
Context: Closed
Waits: 200
Context: Open
Waits: 2000
Context: Open
Waits: 2000
Run Code Online (Sandbox Code Playgroud)
handledEventsAllowedBeforeBreaking4是什么时候输出
Context: Closed
Waits: 200
Context: Closed
Waits: 200
Context: Closed
Waits: 200
Context: Open
Waits: 2000
Run Code Online (Sandbox Code Playgroud)