本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
重试失败的活动
活动有时会由于临时原因而失败,例如,连接临时中断。在其他时间,活动可能会成功,因此,处理活动失败的相应方法通常是重试活动,可能会重试很多次。
可以使用几种不同的策略重试活动;最佳的策略取决于您的工作流程详细信息。这些策略分为三种基本类别:
-
该 retry-until-success策略只是不断重试活动,直到活动完成。
-
指数重试策略以指数方式增加重试尝试的时间间隔,直到活动完成或该过程达到指定的停止点,如最大尝试次数。
-
自定义重试策略确定在每个失败尝试后是否或如何重试活动。
以下几节介绍了如何实施这些策略。示例工作流程工作线程均使用单个活动 (unreliableActivity),它随机执行以下操作之一:
-
立即完成
-
有意超过超时值而失败
-
有意引发
IllegalStateException而失败
Retry-Until-Success 策略
最简单的重试策略是在每次失败时不断重试活动,直到最终成功。基本模式如下:
-
在工作流程的入口点方法中实现施嵌套的
TryCatch或TryCatchFinally类。 -
在
doTry中执行活动 -
如果活动失败,该框架调用
doCatch,这会再次运行入口点方法。 -
重复步骤 2-3,直到活动成功完成。
以下工作流程实现了该 retry-until-success策略。工作流程接口是在 RetryActivityRecipeWorkflow 中实现的,并具有一个方法 (runUnreliableActivityTillSuccess),它是工作流程的入口点。工作流程工作线程是在 RetryActivityRecipeWorkflowImpl 中实现的,如下所示:
public class RetryActivityRecipeWorkflowImpl implements RetryActivityRecipeWorkflow { @Override public void runUnreliableActivityTillSuccess() { final Settable<Boolean> retryActivity = new Settable<Boolean>(); new TryCatch() { @Override protected void doTry() throws Throwable { Promise<Void> activityRanSuccessfully = client.unreliableActivity(); setRetryActivityToFalse(activityRanSuccessfully, retryActivity); } @Override protected void doCatch(Throwable e) throws Throwable { retryActivity.set(true); } }; restartRunUnreliableActivityTillSuccess(retryActivity); } @Asynchronous private void setRetryActivityToFalse( Promise<Void> activityRanSuccessfully, @NoWait Settable<Boolean> retryActivity) { retryActivity.set(false); } @Asynchronous private void restartRunUnreliableActivityTillSuccess( Settable<Boolean> retryActivity) { if (retryActivity.get()) { runUnreliableActivityTillSuccess(); } } }
工作流程的工作方式如下所示:
-
runUnreliableActivityTillSuccess创建一个名为retryActivity的Settable<Boolean>对象,它用于指示活动是否失败并应进行重试。Settable<T>派生自Promise<T>,它的工作方式基本相同,但您手动设置Settable<T>对象的值。 -
runUnreliableActivityTillSuccess实现一个匿名的嵌套TryCatch类以处理unreliableActivity活动引发的任何异常。有关如何处理异步代码引发的异常的详细讨论,请参阅错误处理。 -
doTry执行unreliableActivity活动,它返回一个名为activityRanSuccessfully的Promise<Void>对象。 -
doTry调用异步setRetryActivityToFalse方法,它使用两个参数:-
activityRanSuccessfully使用unreliableActivity活动返回的Promise<Void>对象。 -
retryActivity使用retryActivity对象。
如果
unreliableActivity完成,activityRanSuccessfully将变为就绪状态setRetryActivityToFalse并将retryActivity设置为 false。否则,activityRanSuccessfully从不变为就绪状态并且setRetryActivityToFalse不执行。 -
-
如果
unreliableActivity引发异常,该框架将调用doCatch并为其传递异常对象。doCatch将retryActivity设置为 true。 -
runUnreliableActivityTillSuccess调用异步restartRunUnreliableActivityTillSuccess方法并为其传递retryActivity对象。由于retryActivity具有Promise<T>类型,restartRunUnreliableActivityTillSuccess将推迟执行,直到retryActivity准备就绪,在TryCatch完成后将变为该状态。 -
在
retryActivity准备就绪时,restartRunUnreliableActivityTillSuccess提取该值。-
如果值为
false,则重试成功。restartRunUnreliableActivityTillSuccess不执行任何操作,并终止重试序列。 -
如果值为 true,则重试失败。
restartRunUnreliableActivityTillSuccess调用runUnreliableActivityTillSuccess以再次执行活动。
-
-
重复步骤 1-7,直到
unreliableActivity完成。
注意
doCatch 不处理异常;它仅将 retryActivity 对象设置为 true 以指示活动失败。重试是由异步 restartRunUnreliableActivityTillSuccess 方法处理的,它推迟执行,直到 TryCatch 完成。使用这种方法的原因是,如果在 doCatch 中重试活动,则无法取消该活动。如果在 restartRunUnreliableActivityTillSuccess 中重试活动,则可以执行可取消的活动。
指数重试策略
在使用指数重试策略时,该框架在指定的时间段 (N 秒) 后再次执行失败的活动。如果该尝试失败,该框架在 2N 秒后再次执行该活动,然后在 4N 秒后再次执行该活动,依此类推。由于等待时间可能会变得很长,您通常会在某个时间点停止重试尝试,而不是无限期继续执行。
该框架提供三种方法以实施指数重试策略:
-
@ExponentialRetry注释是最简单的方法,但您必须在编译时设置重试配置选项。 -
RetryDecorator类允许您在运行时设置重试配置,并根据需要对其进行更改。 -
AsyncRetryingExecutor类允许您在运行时设置重试配置,并根据需要对其进行更改。此外,该框架调用用户实现的AsyncRunnable.run方法以运行每个重试尝试。
所有方法都支持以下配置选项,其中时间值以秒为单位:
-
初始重试等待时间。
-
退避系数,它用于计算重试间隔,如下所示:
retryInterval = initialRetryIntervalSeconds * Math.pow(backoffCoefficient, numberOfTries - 2)默认值是 2.0。
-
最大重试次数。默认值为无限制。
-
最大重试间隔。默认值为无限制。
-
过期时间。在重试过程的总持续时间超过该值时,重试尝试将停止。默认值为无限制。
-
将触发重试过程的异常。默认情况下,每个异常都会触发重试过程。
-
不会触发重试尝试的异常。默认情况下,不会排除任何异常。
以下几节介绍了可实施指数重试策略的各种方法。
使用 @ 进行指数重试 ExponentialRetry
为活动实施指数重试策略的最简单方法是,在接口定义中将 @ExponentialRetry 注释应用于活动。如果活动失败,该框架根据指定的选项值自动处理重试过程。基本模式如下:
-
将
@ExponentialRetry应用于相应的活动并指定重试配置。 -
如果注释的活动失败,该框架根据注释的参数指定的配置自动重试活动。
ExponentialRetryAnnotationWorkflow 工作流程工作线程使用 @ExponentialRetry 注释实施指数重试策略。它使用在 ExponentialRetryAnnotationActivities 中实现接口定义的 unreliableActivity 活动,如下所示:
@Activities(version = "1.0") @ActivityRegistrationOptions( defaultTaskScheduleToStartTimeoutSeconds = 30, defaultTaskStartToCloseTimeoutSeconds = 30) public interface ExponentialRetryAnnotationActivities { @ExponentialRetry( initialRetryIntervalSeconds = 5, maximumAttempts = 5, exceptionsToRetry = IllegalStateException.class) public void unreliableActivity(); }
@ExponentialRetry 选项指定以下策略:
-
仅在活动引发
IllegalStateException时重试。 -
使用 5 秒初始等待时间。
-
不超过 5 次重试尝试。
工作流程接口是在 RetryWorkflow 中实现的,并具有一个方法 (process),它是工作流程的入口点。工作流程工作线程是在 ExponentialRetryAnnotationWorkflowImpl 中实现的,如下所示:
public class ExponentialRetryAnnotationWorkflowImpl implements RetryWorkflow { public void process() { handleUnreliableActivity(); } public void handleUnreliableActivity() { client.unreliableActivity(); } }
工作流程的工作方式如下所示:
-
process运行同步handleUnreliableActivity方法。 -
handleUnreliableActivity执行unreliableActivity活动。
如果活动引发 IllegalStateException 而失败,该框架自动运行在 ExponentialRetryAnnotationActivities 中指定的重试策略。
对类进行指数重试 RetryDecorator
@ExponentialRetry 易于使用。不过,配置是静态的,并且是在编译时设置的,因此,每次活动失败时,该框架使用相同的重试策略。您可以使用 RetryDecorator 类实施更灵活的指数重试策略,它允许在运行时指定配置并根据需要进行更改。基本模式如下:
-
创建并配置一个
ExponentialRetryPolicy对象以指定重试配置。 -
创建一个
RetryDecorator对象,并将步骤 1 中的ExponentialRetryPolicy对象传递给构造函数。 -
将活动客户端的类名传递给
RetryDecorator对象的 decorate 方法,以将装饰器对象应用于活动。 -
执行活动。
如果活动失败,该框架根据 ExponentialRetryPolicy 对象的配置重试活动。您可以根据需要修改该对象以更改重试配置。
注意
@ExponentialRetry 注释和 RetryDecorator 类相互排斥。您无法使用 RetryDecorator 动态覆盖 @ExponentialRetry 注释指定的重试策略。
以下工作流程实现说明了如何使用 RetryDecorator 类实施指数重试策略。它使用一个没有 @ExponentialRetry 注释的 unreliableActivity 活动。工作流程接口是在 RetryWorkflow 中实现的,并具有一个方法 (process),它是工作流程的入口点。工作流程工作线程是在 DecoratorRetryWorkflowImpl 中实现的,如下所示:
public class DecoratorRetryWorkflowImpl implements RetryWorkflow { ... public void process() { long initialRetryIntervalSeconds = 5; int maximumAttempts = 5; ExponentialRetryPolicy retryPolicy = new ExponentialRetryPolicy( initialRetryIntervalSeconds).withMaximumAttempts(maximumAttempts); Decorator retryDecorator = new RetryDecorator(retryPolicy); client = retryDecorator.decorate(RetryActivitiesClient.class, client); handleUnreliableActivity(); } public void handleUnreliableActivity() { client.unreliableActivity(); } }
工作流程的工作方式如下所示:
-
process使用以下方法创建并配置一个ExponentialRetryPolicy对象:-
将初始重试间隔传递给构造函数。
-
调用对象的
withMaximumAttempts方法以将最大尝试次数设置为 5。ExponentialRetryPolicy公开了其他with对象,您可以使用这些对象指定其他配置选项。
-
-
process创建一个名为retryDecorator的RetryDecorator对象,并将步骤 1 中的ExponentialRetryPolicy对象传递给构造函数。 -
process调用retryDecorator.decorate方法并为其传递活动客户端的类名,以将装饰器应用于活动。 -
handleUnreliableActivity执行活动。
如果活动失败,该框架根据步骤 1 中指定的配置重试活动。
注意
ExponentialRetryPolicy 类的几个 with 方法具有相应的 set 方法,您可以随时调用该方法以修改相应的配置选项:setBackoffCoefficient、setMaximumAttempts、setMaximumRetryIntervalSeconds 和 setMaximumRetryExpirationIntervalSeconds。
对类进行指数重试 AsyncRetryingExecutor
RetryDecorator 类在配置重试过程方面提供比 @ExponentialRetry 更大的灵活性,但该框架仍会根据 ExponentialRetryPolicy 对象的当前配置自动运行重试尝试。更灵活的方法是使用 AsyncRetryingExecutor 类。除了允许您在运行时配置重试过程以外,该框架还会调用用户实现的 AsyncRunnable.run 方法以运行每个重试尝试,而不是直接执行活动。
基本模式如下:
-
创建并配置一个
ExponentialRetryPolicy对象以指定重试配置。 -
创建一个
AsyncRetryingExecutor对象,并为其传递ExponentialRetryPolicy对象以及一个工作流程时钟实例。 -
实现一个匿名的嵌套
TryCatchFinally或TryCatch类。 -
实现一个匿名的
AsyncRunnable类,并覆盖run方法以实现用于运行活动的自定义代码。 -
覆盖
doTry以调用AsyncRetryingExecutor对象的execute方法,并为其传递步骤 4 中的AsyncRunnable类。AsyncRetryingExecutor对象调用AsyncRunnable.run以运行活动。 -
如果活动失败,则
AsyncRetryingExecutor对象根据步骤 1 中指定的重试策略再次调用AsyncRunnable.run方法。
以下工作流程说明了如何使用 AsyncRetryingExecutor 类实施指数重试策略。它使用与前面讨论的 DecoratorRetryWorkflow 工作流程相同的 unreliableActivity 活动。工作流程接口是在 RetryWorkflow 中实现的,并具有一个方法 (process),它是工作流程的入口点。工作流程工作线程是在 AsyncExecutorRetryWorkflowImpl 中实现的,如下所示:
public class AsyncExecutorRetryWorkflowImpl implements RetryWorkflow { private final RetryActivitiesClient client = new RetryActivitiesClientImpl(); private final DecisionContextProvider contextProvider = new DecisionContextProviderImpl(); private final WorkflowClock clock = contextProvider.getDecisionContext().getWorkflowClock(); public void process() { long initialRetryIntervalSeconds = 5; int maximumAttempts = 5; handleUnreliableActivity(initialRetryIntervalSeconds, maximumAttempts); } public void handleUnreliableActivity(long initialRetryIntervalSeconds, int maximumAttempts) { ExponentialRetryPolicy retryPolicy = new ExponentialRetryPolicy(initialRetryIntervalSeconds).withMaximumAttempts(maximumAttempts); final AsyncExecutor executor = new AsyncRetryingExecutor(retryPolicy, clock); new TryCatch() { @Override protected void doTry() throws Throwable { executor.execute(new AsyncRunnable() { @Override public void run() throws Throwable { client.unreliableActivity(); } }); } @Override protected void doCatch(Throwable e) throws Throwable { } }; } }
工作流程的工作方式如下所示:
-
process调用handleUnreliableActivity方法并为其传递配置设置。 -
handleUnreliableActivity使用步骤 1 中的配置设置创建ExponentialRetryPolicy对象retryPolicy。 -
handleUnreliableActivity创建AsyncRetryExecutor对象executor,并将步骤 2 中的ExponentialRetryPolicy对象以及一个工作流程时钟实例传递给构造函数。 -
handleUnreliableActivity实现一个匿名的嵌套TryCatch类,并覆盖doTry和doCatch方法以运行重试尝试并处理任何异常。 -
doTry创建一个匿名的AsyncRunnable类,并覆盖run方法以实现自定义代码以执行unreliableActivity。为简单起见,run仅执行活动,但您可以根据需要实现更复杂的方法。 -
doTry调用executor.execute并将其传递到AsyncRunnable对象。execute调用AsyncRunnable对象的run方法以运行活动。 -
如果活动失败,执行程序根据
retryPolicy对象配置再次调用run。
有关如何使用 TryCatch 类处理错误的详细讨论,请参阅 AWS Flow Framework 对于 Java 异常。
自定义重试策略
重试失败活动的最灵活的方法是自定义策略,它递归地调用运行重试尝试的异步方法,就像策略一样。 retry-until-success不过,您实现自定义逻辑以确定是否以及如何运行每个连续重试尝试,而不是直接再次运行活动。基本模式如下:
-
创建一个
Settable<T>状态对象,它用于指示活动是否失败。 -
实现一个嵌套
TryCatch或TryCatchFinally类。 -
doTry执行活动。 -
如果活动失败,
doCatch设置该状态对象以指示活动失败。 -
调用一个异步失败处理方法,并为其传递该状态对象。该方法推迟执行,直到
TryCatch或TryCatchFinally完成。 -
该失败处理方法确定是否重试活动,如果重试,确定何时重试活动。
以下工作流程说明如何实现自定义重试策略。它使用与 DecoratorRetryWorkflow 和 AsyncExecutorRetryWorkflow 工作流程相同的 unreliableActivity 活动。工作流程接口是在 RetryWorkflow 中实现的,并具有一个方法 (process),它是工作流程的入口点。工作流程工作线程是在 CustomLogicRetryWorkflowImpl 中实现的,如下所示:
public class CustomLogicRetryWorkflowImpl implements RetryWorkflow { ... public void process() { callActivityWithRetry(); } @Asynchronous public void callActivityWithRetry() { final Settable<Throwable> failure = new Settable<Throwable>(); new TryCatchFinally() { protected void doTry() throws Throwable { client.unreliableActivity(); } protected void doCatch(Throwable e) { failure.set(e); } protected void doFinally() throws Throwable { if (!failure.isReady()) { failure.set(null); } } }; retryOnFailure(failure); } @Asynchronous private void retryOnFailure(Promise<Throwable> failureP) { Throwable failure = failureP.get(); if (failure != null && shouldRetry(failure)) { callActivityWithRetry(); } } protected Boolean shouldRetry(Throwable e) { //custom logic to decide to retry the activity or not return true; } }
工作流程的工作方式如下所示:
-
process调用异步callActivityWithRetry方法。 -
callActivityWithRetry创建一个名为 failure 的Settable<Throwable>对象,此对象用于指示活动是否失败。Settable<T>派生自Promise<T>,它的工作方式基本相同,但您手动设置Settable<T>对象的值。 -
callActivityWithRetry实现一个匿名的嵌套TryCatchFinally类以处理unreliableActivity引发的任何异常。有关如何处理异步代码引发的异常的详细讨论,请参阅AWS Flow Framework 对于 Java 异常。 -
doTry执行unreliableActivity。 -
如果
unreliableActivity引发异常,则该框架将调用doCatch并将其传递给异常对象。doCatch将failure设置为异常对象(指示活动失败)并使该对象处于就绪状态。 -
doFinally检查failure是否准备就绪,只有在doCatch设置了failure时,它才为 true。-
如果准备就绪,
failure则什么都doFinally不做。 -
如果
failure未准备就绪,则活动完成并且doFinally将 failure 设置为null。
-
-
callActivityWithRetry调用异步retryOnFailure方法并为其传递 failure。由于 failure 具有Settable<T>类型,callActivityWithRetry将推迟执行,直到 failure 准备就绪,在TryCatchFinally完成后将变为该状态。 -
retryOnFailure从 failure 中获取值。-
如果 failure 设置为 null,则重试尝试成功。
retryOnFailure不执行任何操作,这会终止重试过程。 -
如果 failure 设置为一个异常对象并且
shouldRetry返回 true,则retryOnFailure调用callActivityWithRetry以重试活动。shouldRetry实现自定义逻辑以确定是否重试失败的活动。为简单起见,shouldRetry始终返回true并且retryOnFailure立即执行活动,但您可以根据需要实现更复杂的逻辑。
-
-
重复步骤 2 到 8,直至
unreliableActivity完成shouldRetry决定停止该过程。
注意
doCatch 不处理重试过程;它仅设置 failure 以指示活动失败。重试过程是由异步 retryOnFailure 方法处理的,它推迟执行,直到 TryCatch 完成。使用这种方法的原因是,如果在 doCatch 中重试活动,则无法取消该活动。如果在 retryOnFailure 中重试活动,则可以执行可取消的活动。