Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.
Reintento de actividades con errores
En ocasiones se producen errores en las actividades por motivos efímeros, como por ejemplo la pérdida temporal de conexión. En otras ocasiones, la actividad podría tener éxito, por lo que la manera apropiada de abordar el error en la actividad consiste con frecuencia en reintentar la actividad, quizás varias veces.
Hay diferentes estrategias para reintentar actividades; la mejor depende de los detalles de su flujo de trabajo. Las estrategias se dividen en tres categorías básicas:
-
La estrategia de reintento hasta tener éxito simplemente sigue reintentando la actividad hasta que la completa.
-
La estrategia de reintento exponencial aumenta el intervalo de tiempo entre reintentos exponencialmente hasta que se completa la actividad o el proceso alcanza un punto de parada especificado, como por ejemplo un número máximo de intentos.
-
La estrategia de reintento personalizada decide si se reintenta la actividad, o cómo hacerlo, después de cada intento en el que se ha producido un error.
Las siguientes secciones describen cómo implementar estas estrategias. Todos los procesos de trabajo de flujo de trabajo de ejemplo utilizan una sola actividad, unreliableActivity
, que hace lo siguiente de manera aleatoria:
-
Se completa de manera inmediata
-
Produce un error de manera intencionada superando el valor del tiempo de espera
-
Produce un error de manera intencionada produciendo una
IllegalStateException
Estrategia de reintento hasta tener éxito
La estrategia de reintento más sencilla consiste en seguir reintentando la actividad cada vez que se produce un error hasta que finalmente se ejecuta satisfactoriamente. Este es el patrón básico:
-
Implemente una clase
TryCatch
oTryCatchFinally
anidada en el método de punto de entrada de su flujo de trabajo. -
Ejecute la actividad en
doTry
-
Si se produce un error en la actividad, el marco de trabajo llama a
doCatch
, que ejecuta de nuevo el método de punto de entrada. -
Repita los pasos 2 y 3 hasta que la actividad se realiza correctamente.
El siguiente flujo de trabajo implementa la estrategia de reintento hasta tener éxito. La interfaz de flujo de trabajo se implementa en RetryActivityRecipeWorkflow
y tiene un método, runUnreliableActivityTillSuccess
, que es el punto de entrada del flujo de trabajo. El proceso de trabajo de flujo de trabajo se implementa en RetryActivityRecipeWorkflowImpl
, de la siguiente manera:
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(); } } }
El flujo de trabajo funciona de la siguiente manera:
-
runUnreliableActivityTillSuccess
crea un objetoSettable<Boolean>
denominadoretryActivity
que se utiliza para indicar si se ha producido un error en la actividad y debería volver a intentarse.Settable<T>
proviene dePromise<T>
y funciona de forma muy parecida, pero en este caso, usted establece manualmente el valor de un objetoSettable<T>
. -
runUnreliableActivityTillSuccess
implementa una claseTryCatch
anidada de manera anónima para gestionar cualquier excepción lanzada por la actividadunreliableActivity
. Para obtener más información sobre cómo tratar excepciones lanzadas por un código asíncrono, consulte Control de errores. -
doTry
ejecuta la actividadunreliableActivity
, que devuelve un objetoPromise<Void>
denominadoactivityRanSuccessfully
. -
doTry
llama al método asíncronosetRetryActivityToFalse
, que tiene dos parámetros:-
activityRanSuccessfully
toma el objetoPromise<Void>
devuelto por la actividadunreliableActivity
. -
retryActivity
toma el objetoretryActivity
.
Si
unreliableActivity
finaliza,activityRanSuccessfully
está listo ysetRetryActivityToFalse
estableceretryActivity
en "false". De lo contrario,activityRanSuccessfully
nunca está listo ysetRetryActivityToFalse
no se ejecuta. -
-
Si
unreliableActivity
genera una excepción, el marco de trabajo llama adoCatch
y le pasa el objeto de excepción.doCatch
estableceretryActivity
en true. -
runUnreliableActivityTillSuccess
al método asíncronorestartRunUnreliableActivityTillSuccess
y le pasa el objetoretryActivity
. Dado queretryActivity
es un tipo dePromise<T>
,restartRunUnreliableActivityTillSuccess
aplaza la ejecución hasta queretryActivity
esté listo, lo que ocurre después de que se completaTryCatch
. -
Cuando
retryActivity
está listo,restartRunUnreliableActivityTillSuccess
extrae el valor.-
Si el valor es
false
, el reintento ha tenido éxito.restartRunUnreliableActivityTillSuccess
no hace nada y la secuencia de reintento termina. -
Si el valor es "true", se ha producido un error en el reintento.
restartRunUnreliableActivityTillSuccess
llama arunUnreliableActivityTillSuccess
para ejecutar de nuevo la actividad.
-
-
Los pasos 1 al 7 se repiten hasta que se completa
unreliableActivity
.
nota
doCatch
no gestiona la excepción; simplemente establece el objeto retryActivity
en "true" para indicar que se ha producido un error en la actividad. El reintento es gestionado por el método asíncrono restartRunUnreliableActivityTillSuccess
, que aplaza la ejecución hasta que se completa TryCatch
. El motivo de este enfoque es que, si reintenta una actividad en doCatch
, no es posible cancelarla. Volver a intentar la actividad restartRunUnreliableActivityTillSuccess
le permite ejecutar actividades que se pueden cancelar.
Estrategia de reintento exponencial
Con la estrategia de reintento exponencial, el marco de trabajo ejecuta una actividad en la que se ha producido un error de nuevo tras un periodo de tiempo especificado, N segundos. Si se produce un error en ese intento, el marco de trabajo ejecuta de nuevo la actividad después de 2N segundos y luego tras 4N segundos, etc. Debido a que el tiempo de espera puede aumentar bastante, habitualmente interrumpe los reintentos en algún punto en lugar de continuar de manera indefinida.
El marco de trabajo ofrece tres maneras de implementar una estrategia de reintento exponencial:
-
La anotación
@ExponentialRetry
es el enfoque más sencillo, pero debe establecer las opciones de configuración de los reintentos en tiempo de compilación. -
La clase
RetryDecorator
le permite establecer la configuración de reintentos en el tiempo de ejecución y cambiarla según sea necesario. -
La clase
AsyncRetryingExecutor
le permite establecer la configuración de reintentos en el tiempo de ejecución y cambiarla según sea necesario. Además, el marco de trabajo llama a un métodoAsyncRunnable.run
implementado por el usuario para la ejecución de cada reintento.
Todos los enfoques admiten las siguientes opciones de configuración, en las que los valores de tiempo se muestran en segundos:
-
El tiempo de espera de reintento inicial.
-
El coeficiente de retardo, que se utiliza para computar los intervalos de reintento, de la siguiente manera:
retryInterval = initialRetryIntervalSeconds * Math.pow(backoffCoefficient, numberOfTries - 2)
El valor predeterminado es 2.0.
-
El número máximo de reintentos. El valor predeterminado es ilimitado.
-
El intervalo máximo de reintentos. El valor predeterminado es ilimitado.
-
El plazo de vencimiento. Los reintentos se detienen cuando la duración total del proceso supera este valor. El valor predeterminado es ilimitado.
-
Las excepciones que dispararán el proceso de reintento. De manera predeterminada, todas las excepciones disparan el proceso de reintento.
-
Las excepciones que no dispararán un reintento. De manera predeterminada, no se excluye ninguna excepción.
En las siguientes secciones se describen las distintas maneras de implementar una estrategia de reintento exponencial.
Reintento exponencial con @ExponentialRetry
La manera más sencilla de implementar una estrategia de reintento exponencial para una actividad consiste en aplicar una anotación @ExponentialRetry
a la actividad en la definición de interfaz. Si se produce un error en la actividad, el marco de trabajo gestiona el proceso de reintento automáticamente, en función de los valores de opciones especificados. Este es el patrón básico:
-
Aplique
@ExponentialRetry
a las actividades apropiadas y especifique la configuración de reintento. -
Si se produce un error en una actividad anotada, el marco de trabajo reintenta automáticamente la actividad en función de la configuración especificada por los argumentos del comentario.
El proceso de trabajo del flujo de trabajo ExponentialRetryAnnotationWorkflow
implementa la estrategia de reintento exponencial utilizando una anotación @ExponentialRetry
. Utiliza una actividad unreliableActivity
cuya definición de interfaz se implementa en ExponentialRetryAnnotationActivities
, de la siguiente manera:
@Activities(version = "1.0") @ActivityRegistrationOptions( defaultTaskScheduleToStartTimeoutSeconds = 30, defaultTaskStartToCloseTimeoutSeconds = 30) public interface ExponentialRetryAnnotationActivities { @ExponentialRetry( initialRetryIntervalSeconds = 5, maximumAttempts = 5, exceptionsToRetry = IllegalStateException.class) public void unreliableActivity(); }
Las opciones de @ExponentialRetry
especifican la siguiente estrategia:
-
Reintentar solo si la actividad lanza
IllegalStateException
. -
Usar el tiempo de espera inicial de 5 segundos.
-
No más de 5 reintentos.
La interfaz de flujo de trabajo se implementa en RetryWorkflow
y tiene un método, process
, que es el punto de entrada del flujo de trabajo. El proceso de trabajo de flujo de trabajo se implementa en ExponentialRetryAnnotationWorkflowImpl
, de la siguiente manera:
public class ExponentialRetryAnnotationWorkflowImpl implements RetryWorkflow { public void process() { handleUnreliableActivity(); } public void handleUnreliableActivity() { client.unreliableActivity(); } }
El flujo de trabajo funciona de la siguiente manera:
-
process
ejecuta el método síncronohandleUnreliableActivity
. -
handleUnreliableActivity
ejecuta la actividadunreliableActivity
.
Si se produce un error en la actividad y lanza IllegalStateException
, el marco de trabajo ejecuta automáticamente la estrategia de reintento especificada en ExponentialRetryAnnotationActivities
.
Reintento exponencial con la clase RetryDecorator
@ExponentialRetry
es fácil de utilizar. No obstante, la configuración es estática y se establece en el tiempo de compilación, por lo que el marco de trabajo utiliza la misma estrategia de reintento cada vez que se produce un error en la actividad. Puede implementar una estrategia de reintento exponencial más flexible utilizando la clase RetryDecorator
que le permite especificar la configuración en el tiempo de ejecución y cambiarla según sea necesario. Este es el patrón básico:
-
Cree y configure un objeto
ExponentialRetryPolicy
que especifique la configuración de reintento. -
Cree un objeto
RetryDecorator
y pase el objetoExponentialRetryPolicy
del Paso 1 al constructor. -
Aplique el objeto decorador a la actividad pasando el nombre de la clase del cliente de la actividad al método de decoración del objeto
RetryDecorator
. -
Ejecute la actividad.
Si se produce un error en la actividad, el marco de trabajo reintenta la actividad en función de la configuración del objeto ExponentialRetryPolicy
. Puede cambiar la configuración de los reintentos según sea necesario modificando este objeto.
nota
La anotación @ExponentialRetry
y la clase RetryDecorator
se excluyen mutuamente. No puede utilizar RetryDecorator
para anular dinámicamente una política de reintentos especificada por una anotación @ExponentialRetry
.
La siguiente implementación de flujo de trabajo muestra cómo usar la clase RetryDecorator
para implementar una estrategia de reintento exponencial. Utiliza una actividad unreliableActivity
que no tiene una anotación @ExponentialRetry
. La interfaz de flujo de trabajo se implementa en RetryWorkflow
y tiene un método, process
, que es el punto de entrada del flujo de trabajo. El proceso de trabajo de flujo de trabajo se implementa en DecoratorRetryWorkflowImpl
, de la siguiente manera:
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(); } }
El flujo de trabajo funciona de la siguiente manera:
-
process
crea y configura un objetoExponentialRetryPolicy
de la siguiente manera:-
Pasando el intervalo de reintentos inicial al constructor.
-
Llamando al método
withMaximumAttempts
del objeto para establecer el número máximo de intentos en 5.ExponentialRetryPolicy
expone otros objetoswith
que se pueden utilizar para especificar otras opciones de configuración.
-
-
process
crea un objetoRetryDecorator
denominadoretryDecorator
y pasa el objetoExponentialRetryPolicy
del Paso 1 al constructor. -
process
aplica el decorador a la actividad llamando al métodoretryDecorator.decorate
y pasándole el nombre de la clase del cliente de la actividad. -
handleUnreliableActivity
ejecuta la actividad.
Si se produce un error en la actividad, el marco de trabajo lo reintenta en función de la configuración especificada en el Paso 1.
nota
Varios de los métodos with
de la clase ExponentialRetryPolicy
tienen un método set
correspondiente que puede llamar para modificar la opción de configuración correspondiente en cualquier momento: setBackoffCoefficient
, setMaximumAttempts
, setMaximumRetryIntervalSeconds
y setMaximumRetryExpirationIntervalSeconds
.
Reintento exponencial con la clase AsyncRetryingExecutor
La clase RetryDecorator
ofrece más flexibilidad en la configuración del proceso de reintento que @ExponentialRetry
, pero el marco de trabajo sigue ejecutando los reintentos automáticamente, en función de la configuración actual del objeto ExponentialRetryPolicy
. Un enfoque más flexible consiste en usar la clase AsyncRetryingExecutor
. Además de permitirle configurar el proceso de reintento en el tiempo de ejecución, el marco de trabajo llama a un método AsyncRunnable.run
implementado por el usuario para que ejecute cada reintento en lugar de simplemente ejecutar la actividad.
Este es el patrón básico:
-
Cree y configure un objeto
ExponentialRetryPolicy
para especificar la configuración de reintento. -
Cree un objeto
AsyncRetryingExecutor
y pásele el objetoExponentialRetryPolicy
y una instancia del reloj del flujo de trabajo. -
Implemente una clase
TryCatch
oTryCatchFinally
anidada anónima. -
Implemente una clase
AsyncRunnable
anónima y anule el métodorun
para la implementación del código personalizado para la ejecución de la actividad. -
Anule
doTry
para llamar al métodoexecute
del objetoAsyncRetryingExecutor
y pasarle la claseAsyncRunnable
del Paso 4. El objetoAsyncRetryingExecutor
llama aAsyncRunnable.run
para ejecutar la actividad. -
Si se produce un error en la actividad, el objeto
AsyncRetryingExecutor
llama de nuevo al métodoAsyncRunnable.run
en función de la política de reintentos especificada en el Paso 1.
El siguiente flujo de trabajo muestra cómo usar la clase AsyncRetryingExecutor
para implementar una estrategia de reintento exponencial. Utiliza la misma actividad unreliableActivity
que el flujo de trabajo DecoratorRetryWorkflow
sobre el que hemos hablado antes. La interfaz de flujo de trabajo se implementa en RetryWorkflow
y tiene un método, process
, que es el punto de entrada del flujo de trabajo. El proceso de trabajo de flujo de trabajo se implementa en AsyncExecutorRetryWorkflowImpl
, de la siguiente manera:
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 { } }; } }
El flujo de trabajo funciona de la siguiente manera:
-
process
llama al métodohandleUnreliableActivity
y le pasa los ajustes de la configuración. -
handleUnreliableActivity
utiliza los ajustes de la configuración del Paso 1 para crear un objetoExponentialRetryPolicy
,retryPolicy
. -
handleUnreliableActivity
crea un objetoAsyncRetryExecutor
,executor
, y pasa el objetoExponentialRetryPolicy
del Paso 2 y una instancia del reloj del flujo de trabajo al constructor. -
handleUnreliableActivity
implementa una claseTryCatch
anidada de manera anónima y anula los métodosdoTry
ydoCatch
para ejecutar los reintentos y gestionar cualquier excepción. -
doTry
crea una claseAsyncRunnable
anónima y anula el métodorun
para la implementación del código personalizado para la ejecución deunreliableActivity
. Para simplificar,run
simplemente ejecuta la actividad, pero puede implementar un enfoque más sofisticado según considere apropiado. -
doTry
llama aexecutor.execute
y le pasa el objetoAsyncRunnable
.execute
llama al métodorun
del objetoAsyncRunnable
para ejecutar la actividad. -
Si se produce un error en la actividad, el ejecutor llama a
run
de nuevo, en función de la configuración del objetoretryPolicy
.
Para obtener más información sobre cómo utilizar la clase TryCatch
para gestionar errores, consulte Excepciones del AWS Flow Framework para Java.
Estrategia de reintento personalizada
El enfoque más flexible para el reintento de actividades en las que se ha producido un error es una estrategia personalizada, que llama de forma recursiva a un método asíncrono que ejecuta el reintento, de forma parecida a la estrategia de reintento hasta alcanzar el éxito. No obstante, en lugar de simplemente ejecutar la actividad de nuevo, usted implementa la lógica personalizada que decide si se ejecuta cada reintento sucesivo y cómo hacerlo. Este es el patrón básico:
-
Cree un objeto de estado
Settable<T>
que se utiliza para indicar si se ha producido un error en la actividad. -
Implemente una clase
TryCatch
oTryCatchFinally
anidada. -
doTry
ejecuta la actividad. -
Si se produce un error en la actividad,
doCatch
establece el objeto de estado para indicar que se ha producido un error en la actividad. -
Llame al método asíncrono de gestión de errores y pásele el objeto de estado. El método aplaza la ejecución hasta que
TryCatch
oTryCatchFinally
se completan. -
El método de gestión de errores decide si se vuelve a intentar la actividad y, si la respuesta es afirmativa, cuándo hacerlo.
El siguiente flujo de trabajo muestra cómo implementar una estrategia de reintento personalizada. Utiliza la misma actividad unreliableActivity
que los flujos de trabajo DecoratorRetryWorkflow
y AsyncExecutorRetryWorkflow
. La interfaz de flujo de trabajo se implementa en RetryWorkflow
y tiene un método, process
, que es el punto de entrada del flujo de trabajo. El proceso de trabajo de flujo de trabajo se implementa en CustomLogicRetryWorkflowImpl
, de la siguiente manera:
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; } }
El flujo de trabajo funciona de la siguiente manera:
-
process
llama al método asíncronocallActivityWithRetry
. -
callActivityWithRetry
crea un objetoSettable<Throwable>
llamado failure que se utiliza para indicar si se ha producido un error en la actividad.Settable<T>
proviene dePromise<T>
y funciona de forma muy parecida, pero en este caso usted establece manualmente el valor de un objetoSettable<T>
. -
callActivityWithRetry
implementa una claseTryCatchFinally
anidada de manera anónima para gestionar cualquier excepción lanzada porunreliableActivity
. Para obtener más información sobre cómo tratar excepciones lanzadas por un código asíncrono, consulte Excepciones del AWS Flow Framework para Java. -
doTry
ejecutaunreliableActivity
. -
Si
unreliableActivity
lanza una excepción, el marco de trabajo llama adoCatch
y le pasa el objeto de excepción.doCatch
establecefailure
en el objeto de excepción, lo que indica que se ha producido un error en la actividad y pone el objeto en el estado ready. -
doFinally
comprueba sifailure
está listo, lo que solo será "true" sidoCatch
ha establecidofailure
.-
Si
failure
está listo,doFinally
no hace nada. -
Si
failure
no está listo, la actividad completada ydoFinally
establecen el error ennull
.
-
-
callActivityWithRetry
llama al método asíncronoretryOnFailure
y le pasa el error. Dado que el error es un tipoSettable<T>
,callActivityWithRetry
la ejecución se aplaza hasta que el error esté listo, lo que ocurre después de que se completaTryCatchFinally
. -
retryOnFailure
obtiene el valor del error.-
Si el error se establece en null, el reintento se ha realizado con éxito.
retryOnFailure
no hace nada, lo cual termina el proceso de reintento. -
Si el error se establece en un objeto de excepción y
shouldRetry
devuelve "true",retryOnFailure
llama acallActivityWithRetry
para reintentar la actividad.shouldRetry
implementa la lógica personalizada para decidir si vuelve a intentar una actividad en la que se ha producido un error. Para simplificar,shouldRetry
siempre devuelvetrue
yretryOnFailure
ejecuta la actividad inmediatamente, pero puede implementar una lógica más sofisticada según considere apropiado.
-
-
Los pasos 2 al 8 se repiten hasta que
unreliableActivity
se completa o bien hasta queshouldRetry
decide interrumpir el proceso.
nota
doCatch
no gestiona el proceso de reintento, simplemente establece el error para indicar que se ha producido un error en la actividad. El proceso de reintento es gestionado por el método asíncrono retryOnFailure
, que aplaza la ejecución hasta que se completa TryCatch
. El motivo de este enfoque es que, si reintenta una actividad en doCatch
, no es posible cancelarla. Volver a intentar la actividad retryOnFailure
le permite ejecutar actividades que se pueden cancelar.