【问题标题】:Polly Circuit Breaker handled and unhandled exceptionsPolly 断路器处理和未处理的异常
【发布时间】:2021-03-07 02:09:21
【问题描述】:

我想使用 Polly 来实现断路器模式。

在文档中,有一个 description of the Half Open state,上面写着:

  • 如果收到已处理的异常,则会重新引发该异常,并且电路会立即转换回打开状态,并在配置的时间跨度内再次保持打开状态。
  • 如果收到未处理的异常,电路将保持半开状态。

我不确定我是否理解已处理异常和未处理异常之间的区别。我们正在描述一个操作由策略运行并引发异常的情况。

当他们说异常已处理时,他们的意思是在哪里处理?因为正如我们所说的,action throws 它是不是意味着它没有被处理?

半开状态什么时候保持半开状态,什么时候转为开状态,让我完全搞不懂了。

【问题讨论】:

  • 没有例子,我也没有写任何代码。我在问文档
  • @YonatanNir 我已经通过this answer 中的示例应用程序描述了断路器是如何工作的。请检查一下,如果您还有其他问题,请讨论。
  • @PeterCsala 您的回答很棒,我确实了解 CB 的工作原理,但它仍然没有在这里回答我的问题。我仍然不确定这里处理的异常和未处理的异常之间的区别
  • @YonatanNir 我已经留下了答案,我希望它能回答你的问题。如果没有,请告诉我,我会尝试扩展它。

标签: c# polly circuit-breaker


【解决方案1】:

定义断路器策略时,您可以定义CB implementation 应考虑的异常类型。换句话说,您可以列出那些应被视为执行失败并应计入连续失败计数的异常。

您可以结合使用Handle<T>Or<T> 方法调用来定义异常列表。

让我们通过一个简单的例子来审视这个概念:

var retry = Policy
    .Handle<ArgumentException>()
    .Or<NotSupportedException>()
    .WaitAndRetry(5, _ => TimeSpan.FromSeconds(1),
        onRetry: (exception, delay, context) => Console.WriteLine($"{"Retry",-10}{delay,-10:ss\\.fff}: {exception.GetType().Name}"));

var circuitBreaker = Policy
    .Handle<ArgumentException>()
    .CircuitBreaker(2, TimeSpan.FromSeconds(1),
        onBreak: (ex, @break) => Console.WriteLine($"{"Break",-10}{@break,-10:ss\\.fff}: {ex.GetType().Name}"),
        onReset: () => Console.WriteLine($"{"Reset",-10}"),
        onHalfOpen: () => Console.WriteLine($"{"HalfOpen",-10}"));
  • 断路器策略将所有ArgumentExceptions(包括ArgumentNullExceptionArgumentOutOfRangeException)视为已处理异常。
    • 这意味着如果被调用的委托抛出这三个异常之一,那么它将增加连续失败计数,如果达到阈值,它将中断。
  • ArgumentExceptionNotSupportedException 的情况下都会触发重试策略。
    • 如果其中任何一个被抛出,那么它将休眠一秒钟,然后尝试重新执行相同的委托。

因此,从断路器的角度来看,如果抛出 NotSupportedException,则不会考虑>>因此名称未处理。

这就是我们的示例方法的实现方式,它将抛出ArgumentExceptionNotSupportedException

private static int count = 0;
private const int threshold = 3;
static void SampleCall()
{
    count++;
    if (count >= threshold) throw new NotSupportedException();
    throw new ArgumentException("Nothing");
}

政策的使用:

var strategy = Policy.Wrap(retry, circuitBreaker);

try
{
    strategy.Execute(SampleCall);
    Console.WriteLine("Succeeded");
}
catch (NotSupportedException)
{
    Console.WriteLine("Failed");
}

threshold 设置为 3 时的输出

Retry     01.000    : ArgumentException
Break     01.000    : ArgumentException
Retry     01.000    : ArgumentException
HalfOpen
Retry     01.000    : NotSupportedException
Retry     01.000    : NotSupportedException
Retry     01.000    : NotSupportedException
Failed

在 CB 自身转移到 HalfOpen 状态后,SampleCall 只抛出 NotSupportedExceptions。这不是由 CB 处理的,这就是它保持 HalfOpen 状态的原因。

threshold 设置为 2 时的输出

Retry     01.000    : ArgumentException
Retry     01.000    : NotSupportedException
Retry     01.000    : NotSupportedException
Retry     01.000    : NotSupportedException
Retry     01.000    : NotSupportedException
Failed

CB 没有中断,因为没有两个连续的ArgumentException。但是重试确实触发了,因为它也处理NotSupportedException

threshold 设置为 4 时的输出

Retry     01.000    : ArgumentException
Break     01.000    : ArgumentException
Retry     01.000    : ArgumentException
HalfOpen
Break     01.000    : ArgumentException
Retry     01.000    : ArgumentException
HalfOpen
Retry     01.000    : NotSupportedException
Retry     01.000    : NotSupportedException
Failed

因为当 CB 处于 HalfOpen 状态时,SampleCall 确实抛出了 ArgumentException,这就是为什么 CB 将其视为已处理异常并将自身从 HalfOpen 转移到 Open

【讨论】:

  • 关于threshold = 2,为什么CB不会坏?有第一次原始调用,然后第一次重试是第二次抛出异常
  • @YonatanNir 1) SampleCall 的初始尝试 2) count 从 0 增加到 1 3) threshold 被检查 4) ArgumentEx 被抛出 5) CB 的失败计数是从 0 增加到 1 6) 重试被触发 7) 重试休眠 8) SampleCall 的第一次重试尝试 9) count 从 1 增加到 2 10) threshold 被检查 11) NotSupportedEx 被抛出 11 ) CB 因为未处理的异常而被跳过 12) 触发重试 13)...
  • 啊好吧好吧..我在脑子里做了这一切,所以我先检查了一下,后来才增加。另一个问题,如果我可以的话.. Policy.Wrap 中的策略顺序确实很重要吗?最右边的参数是最先触发的策略?
  • @YonatanNir 是的,顺序很重要。 Policy.Wrap 可以被视为escalation chain。如果内部无法处理,那么它将传播到外部。最右端的策略是最内部的策略,最左端的策略是最外部的策略。例如,如果您有Wrap(retry, timeout),这意味着每次尝试都有一个超时限制。另一方面,如果您有Wrap(timeout, retry),则意味着您的尝试总和有一个全局超时。
  • 另一个小问题.. 文档说“下一个动作将被视为试验,以确定电路的健康状况:将尝试传递给 .Execute(...) 调用的动作委托. (每个 durationOfBreak.. 将允许额外尝试一次)。这是否意味着实际上有 2 次调用作为测试或 1 次?如果有 2 次,那么只要其中一个成功,电路就会关闭吗?跨度>
猜你喜欢
  • 2011-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多