首页 > 代码库 > 云计算设计模式(十八)——重试模式

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

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


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

背景和问题


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

这些故障一般是自校正的,如果经过一个合适的延迟被重复触发一个故障动作很可能是成功的。例如,数据库服务,它正在处理大量并发请求可以实现节流策略,暂时拒绝,直到它的工作量有所缓和任何进一步的请求。试图访问该数据库的应用程序可能无法连接,但如果它经过一个合适的延迟再次尝试可能会成功。

解决方案


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

如果一个应用程序检测到故障时,它试图将请求发送到远程服务它可以通过使用以下策略处理失败
?如果故障指示故障不是瞬时的或不太成功,如果重复例如所造成的无效提供凭据的认证失败不可能成功的,无论多少次未遂),应用程序中止操作和报告一个合适的异常。
?如果报道具体故障不寻常的罕见的,可能是由于反常的情况,如网络数据包成为损坏,同时它被发送。这种情况下,应用程序可以再次立即重试失败的请求,因为相同的故障是不可能被重复请求将可能是成功的。
?如果故障是由一种更加普遍的连接,或“忙”的失败,网络或服务可能需要在短期内同时连接问题纠正或工作积压被清除应用程序应该等待请求重试一个合适的时间

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

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

图1示出了这种模式。如果尝试后预定数量请求不成功,应用程序应将故障为异常,并相应地处理它

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


应用程序应该所有试图访问远程服务,实现重试政策配套上面列出的策略之一的代码。发送到不同的服务请求会受到不同的政策,有的供应商提供封装这种方法库。这些库通常执行的政策参数化的,应用程序开发人员可以指定,如重试次数重试之间的时间项的值

检测故障和重试失败的操作都应该记录这些故障详细信息的应用程序的代码。这个信息可能是有用的运算符。如果一个服务被频繁报道为不可用或忙往往是因为该服务已耗尽其资源则可以减少与这些故障发生时通过换算该服务的频率例如,如果数据库服务正在不断超载,可能是有利的分区数据库负载分散到多个服务器。

注意:

微软Azure提供了重试模式的广泛支持模式与实践瞬态故障处理块允许应用程序通过一系列的重试策略来处理许多Azure服务瞬态故障微软实体框架版本6提供了用于重新尝试数据库操作此外,许多在AzureService 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

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