Spring Boot 微服务中的断路器(Circuit Breaker)和重试(Retry)有什么区别?

3

我的一个同事问我这个问题:断路器和重试的区别是什么,但我不能正确地回答他。我只知道,如果有大量的请求负载,断路器很有用,但是这也可以使用重试来实现。那么,何时使用断路器,何时重试呢?

另外,是否可能在同一个API上同时使用两种方法?

3个回答

7

几年前,我写了一个弹性目录来描述不同的机制。最初我为同事创建了这个文档,然后我公开分享了它。请允许我在此引用相关部分。

重试

类别: 响应式、事后

重试和尝试之间的关系:n次重试意味着最多n+1次尝试。+1是初始请求,如果失败(无论什么原因),则重试逻辑会启动。换句话说,第0步执行时不会有延迟惩罚。

有些情况下,您请求的操作依赖于某个资源,该资源在某个时间点可能无法访问。换句话说,可能存在暂时性问题,这些问题迟早会消失。这种问题可能会导致瞬态故障。通过重试,您可以在将来的特定时刻尝试重新执行相同的操作以克服这些问题。为了能够使用此机制,必须满足以下标准组:

  • 潜在引入的可观察影响是可以接受的。
  • 该操作可以重新执行,而不会产生任何不可逆的副作用。
  • 与承诺的可靠性相比,引入的复杂性微不足道。

让我们逐一审查它们:

  • “失败”一词表示请求者也可以通过更高的延迟/降低吞吐量等方式观察到效果。如果“惩罚”(延迟或降低性能)是不可接受的,则重试对您来说不是一个选项。
  • 这个要求也被称为幂等操作。如果我多次使用相同的输入调用该操作,则它将产生完全相同的结果。换句话说,该操作的行为就像它仅依赖于其参数,而没有其他对象状态影响结果。
  • 尽管这个条件是最关键的之一,但几乎总是被忽略的。正如总是存在权衡(如果我引入Z,那么它可能会增加X,但可能会降低Y)。
    • 我们应该充分了解它们,否则它会在最意外的时候给我们带来一些不必要的惊喜。

断路器

分类:主动型,事前预防

很难将断路器归类为主动型或者被动型,因为它同时具备这两种特性。它能够检测出给定的下游系统故障(被动型),并且保护下游系统免受新请求的淹没(主动型)。

这是最复杂的设计模式之一,主要因为它使用了不同的状态来定义不同的行为。在我们深入了解细节之前,让我们看看为什么需要使用这个工具:

断路器可以检测失败,并防止应用程序尝试执行注定失败的操作(直到安全重试为止) - Wikipedia

因此,这个工具作为一个 mini 数据和控制平面发挥作用。请求通过该代理进行,该代理检查响应(如果有的话)并计算后续的失败次数。如果达到预定的阈值,传输将暂时停止并立即失败。

  • 为什么这个工具是有用的?

它可以防止级联故障。换句话说,下游系统的短暂故障不应该传播到上游系统。通过隐藏故障,我们实际上防止了连锁反应(多米诺骨牌效应)。

  • 它如何知道短暂故障已经消失?

它必须某种方式确定何时再次作为代理安全运行。例如,它可以使用在原始故障检测期间使用的相同检测机制。因此,它的工作方式如下:在一定时间后,它允许单个请求通过,并检查响应。如果成功,则将下游视为健康。否则,什么也不会改变(没有请求通过此代理),只有计时器被重置。

  • 它使用哪些状态?

断路器可以处于以下任何状态:关闭打开半开

  • Closed:它允许任何请求。它计算连续失败的请求次数。
    • 如果连续失败的次数低于阈值并且下一个请求成功,则计数器将重置为0。
    • 如果达到预定义的阈值,则转换为Open
  • Open:它立即拒绝任何请求。它等待预定义的一段时间。
    • 如果经过了这段时间,则转换为HalfOpen
  • HalfOpen:它只允许一个请求。它检查该请求的响应:
    • 如果响应表明成功,则转换为Closed
    • 如果响应表明失败,则返回到Open

弹性策略

上述两种机制/策略并不是相互排斥的,相反,它们可以通过升级机制进行组合。如果内部策略无法处理问题,则可以向外部策略传播一级。

当您在断路器处于Open状态时尝试执行请求,它将抛出异常。您的重试策略可以触发并调整其睡眠时间(以避免不必要的尝试)。

下游系统还可以通知上游它正在收到太多带有429状态码的请求。断路器也可以为此触发,并使用Retry-After头的值作为其睡眠时间。

因此,本节的整个重点是您可以定义客户端和服务器之间如何共同克服瞬态故障的协议。


1
谢谢 @PeterSmith。 这是我迄今为止看过的最好的解释。 - praddy26
@praddy26 很高兴看到这个消息 :) - Peter Csala

4

重试模式使应用程序可以重试操作以期成功。

断路器模式可以防止应用程序执行可能失败的操作。

重试 - 重试模式在瞬时故障的情况下非常有用。这是什么意思?只持续很短时间的“临时”故障是瞬时故障。网络连接的暂时中断、服务暂时关闭或无响应以及相关超时等都是瞬时故障的示例。

由于故障是瞬时的,稍后重试可能会给我们带来所需的结果。

断路器 - 断路器模式在长时间的故障情况下非常有用。考虑到连接中断或需要一些时间才能修复的服务故障。在这种情况下,如果确实需要一段时间才能从服务器那里得到回应,经常重试可能没有多大用处。断路器模式旨在防止应用程序执行可能失败的操作。

断路器会记录最近故障的数量,并根据预定的阈值确定是否应该将请求发送给受压力的服务器。


那么,断路器什么时候会调用服务器呢?我的意思是,它如何知道客户端服务器现在已经准备好提供服务了呢? 并且,是否可以同时使用断路器和重试呢? - Satyaprakash Nayak
2
关闭 - 当一切正常时,断路器保持关闭状态,所有调用都通过到服务。当故障数量超过预定阈值时,断路器会跳闸并打开。 打开 - 断路器对于未执行函数的调用返回错误。 半开 - 超时后,电路切换到半开状态以测试底层问题是否仍然存在。如果在此半开状态下单个调用失败,则断路器再次被跳闸。如果成功,则断路器重置回正常关闭状态。 - Abhishek Mahajan
嗨,阿布希克,对我来说听起来不错。您能在您的答案中加入这个吗?另外,请回答我的最后一个问题。是否可以同时使用断路器和重试? - Satyaprakash Nayak
当然可以将重试发送到断路器,但值得注意的是,当断路器跳闸时,重试将会快速失败。因此,使用指数退避(例如第一次重试后100毫秒重试,然后是200毫秒,400毫秒,800毫秒,1.6秒等,忽略指数退避的良好实现可能引入的抖动)可能是有意义的。 - Levi Ramsey

0
这里是每个机制的简要概述:
断路器: - 断路器模式旨在防止应用程序反复尝试执行可能失败的操作。 - 它通过临时停止对失败服务的请求来保护系统免受级联故障的影响,从而使其能够恢复。 - 当断路器打开时,请求要么立即被拒绝,要么被重定向到备用方法。
重试: - 重试模式用于自动重新调用先前失败的操作,希望失败是暂时的。 - 它对处理瞬态故障(如网络问题)非常有用,通过重试操作可能会解决这些问题。 - 您可以配置重试次数、退避策略和其他与重试相关的参数。
你可以在它们之间选择哪种方法?
  • 如果你想保护系统免受重复故障并避免对一个失败的服务造成过大压力,可以使用断路器。
  • 如果你想增加暂时性故障成功的机会并在不使用断路器的情况下从中恢复,可以使用重试。
  • 在某些情况下,同时使用这两种机制可能是合适的,特别是在处理复杂的分布式系统时。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接