云计算设计模式(二)——断路器模式

简介: 云计算设计模式(二)——断路器模式处理故障连接到远程服务或资源时,可能需要耗费大量的时间。这种模式可以提高应用程序的稳定性和灵活性。 背景和问题 在分布式环境中,如在云,其中,应用程序执行访问远程资源和服务的操作,有可能对这些操作的失败是由于瞬时故障,如慢的网络连接,超时,或者被过度使用的资源或暂时不可用。

云计算设计模式(二)——断路器模式

处理故障连接到远程服务或资源,可能需要耗费大量的时间这种模式可以提高应用程序的稳定性和灵活性

背景和问题



在分布式环境中,如在云,其中,应用程序执行访问远程资源服务的操作,有可能这些操作的失败是由于瞬时故障,如的网络连接超时,或者过度使用的资源或暂时不可用。这些故障一般之后短时间内纠正自己,一个强大的云应用应该准备使用的策略来处理它们,例如,通过重试模式进行说明。

但是,可以是其中的故障是由于那些不容易预见的突发事件的情况下,这可能需要更长的时间来纠正这些故障连接部分损失到服务完整的故障范围的严重程度在这种情况下,它可能是毫无意义的应用,不断重试执行的操作不太可能成功,而不是应用程序应该很快接受该操作已失败,并相应地处理这个故障

此外,如果一个服务是非常繁忙的在系统中的一个部分出现故障可能会导致连锁故障例如调用一个服务的操作可被配置成实现一个超时,如果该服务无法在这段时间内响应一个失败消息答复。然而,这一策略可能导致许多并发请求相同的操作,直到超时时段期满被阻止。这些被禁止的请求可能会持有关键系统资源,如内存线程,数据库连接等。因此,这些资源可能会耗尽从而导致该系统的其他可能无关的部件,需要使用相同的资源的失败。这些情况下,这将是优选的操作立即失败并且尝试调用服务,如果它可能成功。注意,设置一个较短的超时可能有助于解决此问题,在超时不应该如此之短,以致操作失败的大部分时间即使该请求到服务最终会成功。


解决方案



断路器图案可以防止一个应用程序多次试图执行一个操作,即很可能失败,允许它继续而不等待故障恢复或者浪费CPU周期,而它确定该故障是持久的断路器模式也使应用程序能够检测故障是否已经解决。如果问题似乎已经得到纠正​​,应用程序可以尝试调用操作。



注意:

断路器图案的目的重试模式的不同重试模式使应用程序可以重试操作以期望会成功断路器图案防止应用程序执行一个操作,即很可能失败一个应用程序可以通过使用重试模式,通过一个断路器调用操作结合这两种模式。然而重试逻辑应该是由断路器返回任何异常敏感放弃重试次数,如果断路器指示故障不是瞬时的。



断路器充当可能失败操作的代理。代理应监测最近发生的故障数量,然后使用这个信息来决定是否允许该操作继续进行简单地立即返回一个异常。

代理可以被实现为状态机模拟的电路断路器的功能如下状态:
•关闭从应用程序请求是通过对操作进行路由。代理保持最近的失败次数的计数,并且如果该呼叫到操作不成功,则代理递增该计数如果最近的失败次数超过了一个给定时间周期内的规定的阈值时,该代理将被置于打开状态在这一点上代理启动一个超时定时器,当该定时器期满代理放置到开放状态

注意:

超时定时器的目的是为了给系统时间,纠正允许应用程序尝试再次执行该操作之前导致失败的问题


•打开从应用程序请求立即失败异常返回给应用程序
半开放从应用程序请求数量有限允许通过并调用运行如果这些请求是成功的则假定先前导致故障的故障已修复断路器切换到闭合状态故障计数器被复位如果任何请求失败断路器假设故障仍然存在,因此恢复到打开状态,并重新启动超时定时器,系统的时间再延长,从故障中恢复


注意:

半开的状态是很有用的,以防止恢复服务,突然被淹没的请求。作为服务恢复,也可能是能够支持请求的限制音量,直到恢复完成,但在恢复过程中,海量的工作可能会导致服务超时再次失败。




图1示出了用于一个可能的实现的电路断路器的状态。

图1  - 断路器状态


需要注意的是在图1中所用的封闭状态下的失败计数器基于时间的定期自动复位这有助于防止断路器进入打开状态,如果它经受偶然的失败;使断路器跳闸进入打开状态故障阈值时,故障指定数量的指定的时间间隔期间发生的达到所使用的状态下的成功计数器记录成功尝试调用的操作的数量。断路器恢复到封闭状态连续操作调用中指定数量的已成功如果任何调用失败时,断路器立即进入打开状态,并且成功的计数器其进入状态下一次复位。


Note

如何系统恢复从外部处理,可能通过恢复重新启动故障部件或修理的网络连接。



执行断路器图案增加了稳定性和灵活性,以一个系统,提供稳定性,而系统从故障中恢复,并尽量减少此故障对性能的影响可以帮助快速地拒绝一个操作,即很可能失败,而不是等待操作超时或者不返回的请求,以保持系统的响应时间如果断路器提高每次改变状态的时间的事件,该信息可以被用来监测由断路器保护系统的部件的健康状况以提醒管理员断路器跳闸,以在打开状态

模式是可定制的,并且可以根据可能的故障的性质进行调整。例如,您可以申请增加的超时时间为一个断路器可以放置在打开状态断路器的几秒钟开始,然后,如果故障一直没有解决增加超时几分钟的时间,等等。在某些情况下,而不是打开状态返回故障并提高了异常,也可能是有用的,返回一个缺省值,该值有意义应用

问题和注意事项



在决定如何实现这个模式时,您应考虑以下几点:
异常处理通过断路器调用操作应用程序必须准备好处理,如果该操作不可用的,可以抛出的异常这样的异常处理特定应用程序的方式例如一个应用程序可以暂时降低其功能,调用替换操作来尝试执行相同的任务获得相同的数据或者报告该异常给用户,并要求他们稍后再试
•例外的类型一个请求可能失败的原因有多种其中有一些可能指示更严重的类型的失效比其他例如,一个请求可能会失败,因为远程服务已经崩溃了,可能需要几分钟才能恢复,或失败可能是由于该服务暂时超载造成的超时时间。一种断路器可能能够检查发生的异常类型,并根据这些异常的性质调整策略例如它可能需要超时异常更大数目断路器状态相比失败次数跳闸由于服务完全不可用
日志记录。一个断路器应记录所有失败的请求(也可能是成功的请求),以使管理员能够监视封装了操作的健康。
可恢复性您应该配置断路器与之相匹配的是保护的操作可能恢复模式例如如果断路器保持在打开状态下很长一段时间,也可能产生异常,即使对于失败的原因早已得到了解决。类似地,一个断路器可以振荡并降低应用程序的响应时间,如果它从打开状态到状态太快切换
•测试失败的操作在打开状态下,而不是使用一个计时器来确定何时切换到开放状态下,断路器可代替周期性地查验远程服务资源,以确定它是否已经再次变得可用。这个可以采取企图的形式援引了以前失败的操作,也可以使用由远程服务提供的特殊操作专门用于测试服务的健康状况,所描述的卫生端点监测图案
手动覆盖一个系统中,如果恢复时间一个失败的操作非常可变的,可能是有利的,以提供一个手动复位选项,使管理员能够强行关闭断路器(和复位故障计数器同样,管理员可以强制断路器进入开放状态(并重新启动超时定时器,如果由断路器保护动作暂时不可用。
并发相同的电路断路器可以通过大量的应用程序的并行实例来访问。实施不应该阻塞并发请求或添加过多的开销,以每次调用操作
•资源分化使用单个断路器时,一个类型的资源,如果​​可能有多个潜在的独立供应商要小心例如在数据存储器,其包括多个碎片1分片可以是完全可访问的,而另一个经历一个暂时的问题如果这些情况下错误响应被合二为一,应用程序可能试图访问一些碎片,即使发生故障的可能性高,同时获得其他碎片,即使它是可能成功的可能被堵塞
•加速断路有时失败响应可以包含足够的信息用于断路器的实施知道应当立即跳闸并保持处于跳闸状态最小时间量例如,从过载的共享资源错误响应可以指示立即重试时不推荐使用,并且该应用程序应代替再次尝试几分钟时间


Note

HTTP协议定义的“HTTP503服务不可用”,它可以所请求的服务当前不可用的特定的Web服务器上的被返回的响应。响应可以包括附加信息,例如延迟的预期持续时间。


重播失败的请求在打开状态下,而不是简单的故障很快断路器也可以记录每个请求的详细信息,以轴颈安排这些请求时,远程资源服务变得可用重放。
外部服务不当超时电路断路器可能无法充分保护的应用程序,从失败配置有一个漫长的超时时间对外服务业务如果超时太长,运行一个断路器的螺纹可能被堵塞长时间之前断路器指示操作已失败。这个时候许多其他的应用程序实例可以尝试通过断路器来调用服务,并占用一个显著的线程数之前,他们都失败。


使用这个模式



使用这种模式
•为了防止一个应用程序试图调用一个远程服务访问共享资源,如果​​该操作极有可能失败。

这种模式可能不适合
对于处理的应用程序访问本地专用资源,例如存储器内数据结构在这种环境下使用断路器只会增加开销到您的系统
•作为一个替代品来处理异常在应用程序业务逻辑。

例子

在Web应用中几个页面的已填充了从外部服务中检索数据如果该系统实现了最小的缓存点击率最高的每个页面都会导致往返服务从Web应用程序到服务的连接可以用一个超时时间段(通常为60秒)进行配置,并且如果该服务没有这个时间响应每个网页逻辑将假设该服务不可用,并且抛出异常。

但是,如果服务失败,系统非常繁忙用户可能会被迫等待异常发生时长达60秒最终的资源,如内存,连接和线程可能被耗尽以防止其他用户连接到系统即使它们没有访问检索业务数据的页面

通过添加更多的Web服务器和执行负载均衡扩展,系统可能会延误的点资源趋于枯竭但它不会解决问题,因为用户请求仍然会反应迟钝,所有的Web服务器仍然可以最终耗尽资源

包裹连接到服务,并检索数据的断路器逻辑可以帮助缓解这个问题的影响,并且更优雅处理服务故障用户请求仍然会失败的,但它们将更加迅速地失败,并且资源不会被阻塞

CircuitBreaker类维护有关对象,它实现下面的代码所示ICircuitBreakerStateStore接口电路断路器的状态信息。

interface ICircuitBreakerStateStore
{
  CircuitBreakerStateEnum State { get; }

  Exception LastException { get; }

  DateTime LastStateChangedDateUtc { get; }

  void Trip(Exception ex);

  void Reset();

  void HalfOpen();

  bool IsClosed { get; }
}


 

状态 属性指示 断路器 的当前状态 ,以及由 CircuitBreakerStateEnum 枚举 所定义的 将是 这些值 中的一个 程序 HalfOpen 或者 已关闭 如果电路 断路器闭合 如果其 打开或 半开 IsClosed属性 应该是真实的 跳闸 方法 切换 断路器 为打开状态 的状态,并 记录该 引起 状态变化 异常 所发生的异常 的日期和时间 一起 LastException LastStateChangedDateUtc 属性返回 此信息。 复位 方法关闭 断路器 HalfOpen 方法将 断路器 半开

在该实例中 InMemoryCircuitBreakerStateStore 类包含 ICircuitBreakerStateStore 接口的实现 CircuitBreaker 类创建 这个类 的一个实例 来保存 断路器 的状态

CircuitBreaker ExecuteAction 方法包装 的操作 (在 Action委托 形式) 可能会失败。 该方法 运行时,它 首先检查 断路器的 状态 如果它被 关闭( 当地 IsOpen属性 如果断路器 处于打开状态 半开 ,返回 是假的 ExecuteAction 方法试图 调用 Action委托 如果此操作失败 异常处理程序 执行 TrackException 方法 ,用于设置 该电路 断路器 的状态 通过调用 InMemoryCircuitBreakerStateStore 物体的 行程 的方法打开 下面的代码示例 强调了这一 流程。

 

public class CircuitBreaker
{
  private readonly ICircuitBreakerStateStore stateStore =
    CircuitBreakerStateStoreFactory.GetCircuitBreakerStateStore();

  private readonly object halfOpenSyncObject = new object ();
  ...
  public bool IsClosed { get { return stateStore.IsClosed; } }

  public bool IsOpen { get { return !IsClosed; } }

  public void ExecuteAction(Action action)
  {
    ...
    if (IsOpen)
    {
      // The circuit breaker is Open.
      ... (see code sample below for details)
    }

    // The circuit breaker is Closed, execute the action.
    try
    {
      action();
    }
    catch (Exception ex)
    {
      // If an exception still occurs here, simply 
      // re-trip the breaker immediately.
      this.TrackException(ex);

      // Throw the exception so that the caller can tell
      // the type of exception that was thrown.
      throw;
    }
  }

  private void TrackException(Exception ex)
  {
    // For simplicity in this example, open the circuit breaker on the first exception.
    // In reality this would be more complex. A certain type of exception, such as one
    // that indicates a service is offline, might trip the circuit breaker immediately. 
    // Alternatively it may count exceptions locally or across multiple instances and
    // use this value over time, or the exception/success ratio based on the exception
    // types, to open the circuit breaker.
    this.stateStore.Trip(ex);
  }
}


 

下面的例子显示了执行,如果断路器没有关闭代码前面的例子中省略如果断路器已经开了一段时间长于当地OpenToHalfOpenWaitTime字段中CircuitBreaker类中指定的时间首先检查如果是这种情况,则ExecuteAction方法设置断路器半开然后尝试执行该行动代表所指定的操作

如果操作成功,则断路器复位到闭合状态如果操作失败,则跳闸恢复到打开状态,并且发生被更新,以使断路器将等待进一步期间再次尝试执行该操作之前异常所需的时间。

如果断路器至今只有开放的时间很短小于OpenToHalfOpenWaitTime值时,ExecuteAction方法简单地抛出CircuitBreakerOpenException异常返回引发断路器转换到打开状态的误差。

此外,为了防止断路器试图执行并发呼叫操作,同时半开的,它使用一个锁。试图调用该操作如果断路器是公开进行处理,如后所述,它会失败并异常。

  ...
    if (IsOpen)
    {
      // The circuit breaker is Open. Check if the Open timeout has expired.
      // If it has, set the state to HalfOpen. Another approach may be to simply 
      // check for the HalfOpen state that had be set by some other operation.
      if (stateStore.LastStateChangedDateUtc + OpenToHalfOpenWaitTime < DateTime.UtcNow)
      {
        // The Open timeout has expired. Allow one operation to execute. Note that, in
        // this example, the circuit breaker is simply set to HalfOpen after being 
        // in the Open state for some period of time. An alternative would be to set 
        // this using some other approach such as a timer, test method, manually, and 
        // so on, and simply check the state here to determine how to handle execution
        // of the action. 
        // Limit the number of threads to be executed when the breaker is HalfOpen.
        // An alternative would be to use a more complex approach to determine which
        // threads or how many are allowed to execute, or to execute a simple test 
        // method instead.
        bool lockTaken = false;
        try
        {
          Monitor.TryEnter(halfOpenSyncObject, ref lockTaken)
          if (lockTaken)
          {
            // Set the circuit breaker state to HalfOpen.
            stateStore.HalfOpen();

            // Attempt the operation.
            action();

            // If this action succeeds, reset the state and allow other operations.
            // In reality, instead of immediately returning to the Open state, a counter
            // here would record the number of successful operations and return the
            // circuit breaker to the Open state only after a specified number succeed.
            this.stateStore.Reset();
            return;
          }
          catch (Exception ex)
          {
            // If there is still an exception, trip the breaker again immediately.
            this.stateStore.Trip(ex);

            // Throw the exception so that the caller knows which exception occurred.
            throw;
          }
          finally
          {
            if (lockTaken)
            {
              Monitor.Exit(halfOpenSyncObject);
            }
          }
        }
      }
      // The Open timeout has not yet expired. Throw a CircuitBreakerOpen exception to
      // inform the caller that the caller that the call was not actually attempted, 
      // and return the most recent exception received.
      throw new CircuitBreakerOpenException(stateStore.LastException);
    }
    ...


 

使用CircuitBreaker对象,以保护操作时,应用程序创建CircuitBreaker类的一个实例,并调用ExecuteAction方法,指定操作作为参数来执行该应用程序应该准备,如果操作失败,因为断路器处于打开状态,以赶上CircuitBreakerOpenException例外。下面的代码显示了一个示例

var breaker = new CircuitBreaker();

try
{
  breaker.ExecuteAction(() =>
  {
    // Operation protected by the circuit breaker.
    ...
  });
}
catch (CircuitBreakerOpenException ex)
{
  // Perform some different action when the breaker is open.
  // Last exception details are in the inner exception.
  ...
}
catch (Exception ex)
{
  ...
}


 

本文翻译自MSDN: http://msdn.microsoft.com/en-us/library/dn589784.aspx

 

 

目录
相关文章
|
8月前
|
设计模式 Java 数据库连接
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
243 16
|
8月前
|
设计模式 负载均衡 监控
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
245 0
|
8月前
|
设计模式 监控 Java
并发设计模式实战系列(1):半同步/半异步模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
231 0
|
8月前
|
设计模式 安全 Java
并发设计模式实战系列(12):不变模式(Immutable Object)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第十二章,废话不多说直接开始~
199 0
|
10月前
|
存储 人工智能 物联网
云计算助力医疗信息化,推动智慧医疗新模式。
云计算正深刻改变医疗行业,通过高效数据存储、管理与共享,优化医疗资源分配,推动远程医疗发展。它解决了“数据孤岛”问题,强化了数据安全与隐私保护,同时助力智慧医疗建设,降低信息化成本并提升服务效率。未来,云计算将与5G、AI等技术融合,进一步促进医疗服务智能化与规范化,为医疗行业带来全新可能。
520 7
|
8月前
|
设计模式 算法 Java
设计模式觉醒系列(04)策略模式|简单工厂模式的升级版
本文介绍了简单工厂模式与策略模式的概念及其融合实践。简单工厂模式用于对象创建,通过隐藏实现细节简化代码;策略模式关注行为封装与切换,支持动态替换算法,增强灵活性。两者结合形成“策略工厂”,既简化对象创建又保持低耦合。文章通过支付案例演示了模式的应用,并强调实际开发中应根据需求选择合适的设计模式,避免生搬硬套。最后推荐了JVM调优、并发编程等技术专题,助力开发者提升技能。
|
8月前
|
设计模式 Prometheus 监控
并发设计模式实战系列(20):扇出/扇入模式(Fan-Out/Fan-In)(完结篇)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第二十章,废话不多说直接开始~
288 0
|
12月前
|
设计模式
「全网最细 + 实战源码案例」设计模式——模式扩展(配置工厂)
该设计通过配置文件和反射机制动态选择具体工厂,减少硬编码依赖,提升系统灵活性和扩展性。配置文件解耦、反射创建对象,新增产品族无需修改客户端代码。示例中,`CoffeeFactory`类加载配置文件并使用反射生成咖啡对象,客户端调用时只需指定名称即可获取对应产品实例。
258 40
|
10月前
|
设计模式 Java 关系型数据库
设计模式:工厂方法模式(Factory Method)
工厂方法模式是一种创建型设计模式,通过将对象的创建延迟到子类实现解耦。其核心是抽象工厂声明工厂方法返回抽象产品,具体工厂重写该方法返回具体产品实例。适用于动态扩展产品类型、复杂创建逻辑和框架设计等场景,如日志记录器、数据库连接池等。优点包括符合开闭原则、解耦客户端与具体产品;缺点是可能增加类数量和复杂度。典型应用如Java集合框架、Spring BeanFactory等。
|
12月前
|
设计模式 Java
「全网最细 + 实战源码案例」设计模式——生成器模式
生成器模式(Builder Pattern)是一种创建型设计模式,用于分步骤构建复杂对象。它允许用户通过控制对象构造的过程,定制对象的组成部分,而无需直接实例化细节。该模式特别适合构建具有多种配置的复杂对象。其结构包括抽象建造者、具体建造者、指挥者和产品角色。适用于需要创建复杂对象且对象由多个部分组成、构造过程需对外隐藏或分离表示与构造的场景。优点在于更好的控制、代码复用和解耦性;缺点是增加复杂性和不适合简单对象。实现时需定义建造者接口、具体建造者类、指挥者类及产品类。链式调用是常见应用方式之一。
210 12