启用应用程序来处理预期的临时的失败时。它会尝试连接到透明的重试操作曾经失败期望,失败的原因是瞬时的服务或网络资源这样的模式能够提高应用程序的稳定性。

背景和问题

通信的应用程序在云中执行的元素必须是可能发生这种环境中瞬时故障敏感这些故障包含网络连接的过程中出现时,一个服务忙碌的瞬时损失的组件和服务中,服务暂时不可用超时。

这些故障通常是自校正的,假设经过一个合适的延迟被反复触发一个故障动作非常可能是成功的。

比如,数据库服务,它正在处理大量并发请求能够实现节流策略,临时拒绝。直到它的工作量有所缓和不论什么进一步的请求。

试图訪问该数据库的应用程序可能无法连接,但假设它经过一个合适的延迟再次尝试可能会成功。

解决方式

在云中。瞬时故障的情况并不少见和应用应该被设计为优雅透明地处理它们,降低的影响,这样的故障可能对应用程序正在运行业务任务。

假设一个应用程序检測到故障时,它试图将请求发送到远程服务它能够通过使用下面策略处理失败
假设故障指示故障不是瞬时的或不太成功,假设反复比如所造成的无效提供凭据的认证失败不可能成功的,不管多少次未遂)。应用程序中止操作和报告一个合适的异常。

假设报道详细故障不平常的罕见的,可能是由于反常的情况,如网络数据包成为损坏,同一时候它被发送。这样的情况下。应用程序能够再次马上重试失败的请求,由于同样的故障是不可能被反复请求将可能是成功的。
假设故障是由一种更加普遍的连接,或“忙”的失败,网络或服务可能须要在短期内同一时候连接问题纠正或工作积压被清除应用程序应该等待请求重试一个合适的时间

对于比較常见的短暂故障重试期间。应选择以传播从应用程序尽可能均匀的多个实例的请求。能够降低繁忙的业务持续过载的可能性。假设一个应用程序的多个实例不断轰击重试请求的服务。则可能须要该服务更长的时间来恢复。

假设请求仍然失败。应用程序能够等待进一步的时期,再次尝试假设须要的话这个过程能够反复而添加重试的延迟。直到请求某一最大数目已经尝试都失败了。延迟时间能够逐步添加。使用的定时策略,如指数回退。取决于故障的性质可能性,这将在这段时间内被校正。

图1示出了这样的模式。

假设尝试后预定数量请求不成功,应用程序应将故障为异常,并对应地处理它

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

图1 - 使用重试模式调用托管服务的操作

应用程序应该全部试图訪问远程服务,实现重试政策配套上面列出的策略之中的一个的代码。

发送到不同的服务请求会受到不同的政策。有的供应商提供封装这样的方法库。

这些库通常运行的政策參数化的,应用程序开发者能够指定,如重试次数重试之间的时间项的值

检測故障和重试失败的操作都应该记录这些故障具体信息的应用程序的代码。这个信息可能是实用的运算符。

假设一个服务被频繁报道为不可用或忙往往是由于该服务已耗尽其资源则能够降低与这些故障发生时通过换算该服务的频率比如,假设数据库服务正在不断超载,可能是有利的分区数据库负载分散到多个server。

注意:

微软Azure提供了重试模式的广泛支持

模式与实践瞬态故障处理块同意应用程序通过一系列的重试策略来处理很多Azure服务瞬态故障微软实体框架版本号6提供了用于又一次尝试数据库操作此外。很多在Azure
Service Bus和Azure存储的API透明地运行重试逻辑

问题和注意事项

在决定怎样实现这个模式时。您应考虑下面几点:
重试政策应进行调整,以满足应用故障性质业务需求它可能是更好一些非关键操作失败而不是重试几次。并影响应用程序的吞吐量。

比如试图訪问远程服务的交互式Web应用程序,这可能是更好的重试之后重试之间仅仅有一个短的延迟数量较少失败并显示一个适当的消息给用户(比如,请稍后“)。再次尝试阻止应用程序变得反应迟钝对于批处理应用程序能够是更合适的。以添加重试尝试的次数尝试之间指数添加的延迟
尝试之间最小的延迟大量的重试高攻击重试的政策可能会进一步减少正在接近执行容量的占用。

此重试策略可能会影响应用程序的响应,假设它不断地在尝试执行失败的操作,而不是做实用功
•假设一个显著次数的重试请求仍然失败,则可能是更好的应用程序。以防止进一步的请求将要在同样的资源一个周期。并简单地马上报告故障期限届满后,该应用程序能够临时同意通过一个或多个请求,看看他们是否成功。对于这一策略的具体信息,请參阅断路器格局。

在由它实现了一个重试策略可能须要为幂等的应用程序调用的服务的操作比如发送到服务的请求能够被接收处理成功,可是因为瞬时故障可能无法发送响应,指示该处理已完毕。然后在应用程序重试逻辑可能试图反复没有接收到所述第一请求的假定该请求。
•一个请求到服务失败可能因为各种原因提出不同的异常依据故障的性质。一些例外可指示故障,能够很迅速地得到解决,而还有一些可能表明该故障持续时间更长

可能是故意的重试策略,调整基于所述异常的类型的重试尝试之间的时间

•考虑怎样重试的操作事务的一部分,会影响总体交易的一致性可能是实用的微调对于事务性操作重试政策,最大限度地取得成功的机会,并降低须要撤消全部交易步骤。
•确保全部重试代码是全然针对各种故障条件下进行測试

检查它不会严重影响应用程序的性能或可靠性导致在服务和资源的过度负荷产生竞态条件瓶颈。
•实现仅仅在一个失败的操作方面了解重试逻辑比如,假设包括的重试策略任务调用还有一个任务还包括一个重试策略这个额外的重试的可加长的延迟的处理。它可能是更好的配置的低级任务失败高速并报告失败返回调用它的任务的原因。

然后这个更高级别的任务能够决定怎样处理是依据它自己的策略失效

记录全部的连接故障。提示了重试。使潜在的问题与该应用程序,服务或资源能够被识别是非常重要的
•研究是最有可能发生于一个服务或资源发现,假设它们有可能是持久终端故障。

假设是这种话,它可能是更好地处理该故障为异常。该应用程序能够报告记录该异常。然后试图通过调用还有一个服务,持续或者(假设有一个可用的通过提供降级功能。关于怎样检測和处理持久故障的很多其它信息,请參阅断路器格局。

何时使用这个模式

使用这样的模式
当一个应用程序可能会经历短暂的故障,由于它与远程服务进行交互。或訪问远程资源

这些故障估计将是短暂的并反复曾经没有可以成功兴许尝试的请求。

这样的模式可能不适合
当故障非常可能是持久的。由于可能会影响应用程序的响应性。该应用程序能够简单地是浪费时间和资源试图反复请求最有可能失败
•对于处理故障瞬时故障应用程序的业务逻辑引起错误的内部的异常。
作为一种替代解决系统中的可扩展性问题假设一个应用程序有频繁的“忙”的故障,这是通常指示被訪问的服务或资源应对应加大

样例


实施例说明重试模式的实现

OperationWithBasicRetryAsync方法例如以下所看到的,通过TransientOperationAsync方法异步调用外部服务该方法的细节特定于服务,并从样本代码被省略

private int retryCount = 3;
...

public async Task OperationWithBasicRetryAsync()
{
  int currentRetry = 0;

  for (; ;)
  {
    try
    {
      // Calling external service.
      await TransientOperationAsync();
                    
      // Return or break.
      break;
    }
    catch (Exception ex)
    {
      Trace.TraceError("Operation Exception");

      currentRetry++;

      // Check if the exception thrown was a transient exception
      // based on the logic in the error detection strategy.
      // Determine whether to retry the operation, as well as how 
      // long to wait, based on the retry strategy.
      if (currentRetry > this.retryCount || !IsTransient(ex))
      {
        // If this is not a transient error 
        // or we should not retry re-throw the exception. 
        throw;
      }
    }

    // Wait to retry the operation.
    // Consider calculating an exponential delay here and 
    // using a strategy best suited for the operation and fault.
    Await.Task.Delay();
  }
}

// Async method that wraps a call to a remote service (details not shown).
private async Task TransientOperationAsync()
{
  ...
}

调用此方法的声明被包裹在一个循环一个try/
catch块中封装假设调用TransientOperationAsync方法成功,没有抛出异常for循环退出

假设TransientOperationAsync方法失败,catch块检查失败的原因,而且假设被觉得是一个瞬时错误代码等待一个短暂的延时,然后重试该操作。

在for循环还跟踪该操作已经尝试的次数,而且假设代码失败三次异常被觉得是持久。

假设该异常不是临时的。或者长久的catch处理抛出的异常

此异常退出for循环。应捕获调用该OperationWithBasicRetryAsync方法的代码

IsTransient方法例如以下所看到的,检查是否有特定的一组是相关的。当中所述代码执行环境的异常。

一过异常的定义能够依据被訪问的资源,并其上执行操作环境的不同而不同。

private bool IsTransient(Exception ex)
{
  // Determine if the exception is transient.
  // In some cases this may be as simple as checking the exception type, in other 
  // cases it may be necessary to inspect other properties of the exception.
  if (ex is OperationTransientException)
    return true;

  var webException = ex as WebException;
  if (webException != null)
  {
    // If the web exception contains one of the following status values 
    // it may be transient.
    return new[] {WebExceptionStatus.ConnectionClosed, 
                  WebExceptionStatus.Timeout, 
                  WebExceptionStatus.RequestCanceled }.
            Contains(webException.Status);
  }

  // Additional exception checking logic goes here.
  return false;
}

 

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