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

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

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

背景和问题

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

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

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

解决方案

该断路器图案可以防止一个应用程序多次试图执行一个操作,即很可能失败,允许它继续而不等待故障恢复或者浪费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

 

 

时间: 2024-10-28 19:02:02

云计算设计模式(二)——断路器模式的相关文章

云计算设计模式(十二)——索引表模式

云计算设计模式(十二)--索引表模式 创建索引过的被查询条件经常被引用的数据存储等领域.这种模式可以通过允许应用程序更快速地定位数据来从数据存储中检索提高查询性能. 背景和问题 许多数据存储通过使用主键组织为实体的集合的数据.应用程序可以使用此键来查找和检索数据.图1显示了一个数据存储区保持顾客的信息的例子.主键是客户ID. 图1  - 按主键组织的客户信息(客户ID) 而主键是该取基于该关键字的值的数据的查询宝贵的,应用程序可能不能够使用主键是否需要基于其它字段来检索数据.在顾客例如,应用程序

云计算设计模式(二十四)——仆人键模式

云计算设计模式(二十四)--仆人键模式 使用一个令牌或密钥,向客户提供受限制的直接访问特定的资源或服务,以便由应用程序代码卸载数据传输操作.这个模式是在使用云托管的存储系统或队列的应用中特别有用,并且可以最大限度地降低成本,最大限度地提高可扩展性和性能. 背景和问题 客户端程序和网络浏览器经常需要读取和写入文件或数据流,并从一个应用程序的存储空间.通常,应用程序将处理的运动数据,或者通过从存储读取它,并将其传输到客户端,或通过从客户机读取该载流并将其存储在数据存储中.然而,这种方法吸收了宝贵的资

云计算设计模式(二十二)——静态内容托管模式

云计算设计模式(二十二)--静态内容托管模式 部署静态内容到一个基于云的存储服务,可以直接向客户提供这些.这个模式可以减少潜在的昂贵的计算实例的需求. 景和问题 Web应用程序通常包括静态内容的一些元素.此静态内容可以包括HTML页面和诸如图像和可用到客户端的文件的其他资源,无论是作为一个HTML页的一部分(如嵌入式图像,样式表和客户端JavaScript文件)或作为单独的下载(如PDF文档). 尽管Web服务器以及调整通过有效的动态执行页代码和输出缓存优化的要求,他们仍然必须处理请求下载静态内

云计算设计模式(二十三)——Throttling节流模式

云计算设计模式(二十三)--Throttling节流模式 控制由应用程序使用,一个单独的租户或整个服务的一个实例的资源的消耗.这种模式可以允许系统继续运行并满足服务水平协议,即使当增加需求的资源放置一个极端载荷. 背景和问题 在云应用负载通常上变化的基础上的活动用户的数量或他们正在执行的活动类型的时间.例如,多个用户可能会在工作时间被激活,否则系统可能被要求在每月结束时执行计算昂贵的分析.也有可能是突然和意外的突发活动.如果系统的处理要求超过了可用的资源的能力,其将遭受性能不佳,甚至会失败.该系

云计算设计模式(二十)——调度程序代理管理者模式

云计算设计模式(二十)--调度程序代理管理者模式 协调一系列在分布式服务集和其他远程资源的的行为,试图透明地处理故障,如果这些操作失败,或撤销,如果系统不能从故障中恢复执行工作的影响.这种模式可以分布式系统中增加弹性和灵活性,使之恢复和重试失败是由于短暂的异常,持久的故障和处理故障等操作. 背景和问题 应用程序执行其包括多个步骤,其中的一些可以调用远程服务或访问远程资源的任务.各个步骤可以是相互独立的,但它们是由实现该任务的应用程序逻辑编排. 只要有可能,应用程序应该确保任务运行完成和解决远程访

云计算设计模式(十八)——重试模式

云计算设计模式(十八)--重试模式 启用应用程序来处理预期的,暂时的失败时,它会尝试连接到由透明的重试操作了以前失败的期望,失败的原因是瞬时的服务或网络资源.这种模式可以提高应用程序的稳定性. 背景和问题 该通信的应用程序与在云中运行的元素必须是可能发生在这样的环境中的瞬时故障敏感.这些故障包括网络连接的过程中出现时,一个服务是忙碌的瞬时损失的组件和服务中,服务的临时不可用,或超时. 这些故障一般是自校正的,如果经过一个合适的延迟被重复触发一个故障的动作很可能是成功的.例如,数据库服务,它正在处

云计算设计模式(六)——命令和查询职责分离(CQRS)模式

云计算设计模式(六)--命令和查询职责分离(CQRS)模式 隔离,通过使用不同的接口,从操作读取数据更新数据的操作.这种模式可以最大限度地提高性能,可扩展性和安全性;支持系统在通过较高的灵活性,时间的演变;防止更新命令,从造成合并在域级别上的冲突. 背景和问题 在传统的数据管理系统中,这两个命令(更新数据)和查询(请求数据),针对在一个单一的数据存储库中的相同的一组实体的执行.这些实体可以是在关系数据库中的一个或多个表,如SQL Server的行的子集. 典型地,在这些系统中,所有的创建,读取,

云计算设计模式(三)——补偿交易模式

云计算设计模式(三)--补偿交易模式 撤消由一系列步骤,它们共同限定了最终一致性操作中,如果一个或多个步骤失败执行的工作.按照最终一致性模型,业务实现复杂的业务流程和工作流的云托管的应用程序中很常见. 背景和问题 在云中运行的应用程序频繁修改数据.此数据可跨在各种地理位置的所保持的数据源的一个品种传播.为了避免争用,并提高在分布式环境中,例如这样的性能,应用程序不应该试图提供强事务一致性.相反,应用程序应该实现最终一致性.在该模型中,一个典型的业务操作由一系列的独立的步骤.而正在执行这些步骤的系

云计算设计模式(十五)——管道和过滤器模式

云计算设计模式(十五)--管道和过滤器模式 分解,执行复杂处理成一系列可重复使用分立元件的一个任务.这种模式可以允许执行的处理进行部署和独立缩放任务元素提高性能,可扩展性和可重用性. 背景和问题 一个应用程序可能需要执行各种关于它处理的信息不同复杂的任务.一个简单,但不灵活的方式来实施这个应用程序可以执行此处理为单一模块.然而,这种方法有可能减少用于重构代码,对其进行优化,或者重新使用它,如果是在应用程序中其他地方所需要的相同的处理的部件的机会. 图1通过使用单片式的方式示出了与处理数据的问题.