Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Relance des activités ayant échoué
Les activités échouent parfois pour des raisons éphémères comme une perte temporaire de connectivité. L'activité peut réussir à un autre moment. La méthode appropriée pour résoudre des échecs d'activité consiste donc souvent à relancer l'activité, peut-être plusieurs fois.
Il existe différentes stratégies pour relancer des activités ; celle qui convient le mieux dépend des détails de votre flux de travail. Les stratégies se répartissent en trois catégories de base :
-
La retry-until-success stratégie continue simplement de réessayer l'activité jusqu'à ce qu'elle soit terminée.
-
La stratégie de nouvelle tentative exponentielle augmente de façon exponentielle l'intervalle de temps entre les tentatives jusqu'à ce que l'activité se termine ou que le processus atteigne un point d'arrêt spécifié, comme un nombre maximal de tentatives.
-
La stratégie de nouvelle tentative personnalisée décide s'il faut relancer l'activité après chaque tentative ayant échoué et de quelle manière.
Les sections suivantes expliquent comment implémenter ces stratégies. Les exemples de exécuteurs de flux de travail utilisent tous une activité unique, unreliableActivity, qui exécute de façon aléatoire les actions suivantes :
-
Elle se termine immédiatement
-
Elle échoue intentionnellement en dépassant la valeur de délai d'expiration
-
Elle échoue intentionnellement en déclenchant l'exception
IllegalStateException
Retry-Until-Success Stratégie
La stratégie de nouvelle tentative la plus simple consiste à relancer chaque fois l'activité jusqu'à ce que celle-ci réussisse. Le modèle de base est le suivant :
-
Implémenter une classe
TryCatchouTryCatchFinallyimbriquée dans la méthode de point d'entrée de votre flux de travail. -
Exécuter l'activité dans
doTry. -
Si l'activité échoue, l'infrastructure appelle
doCatch, qui exécute à nouveau la méthode de point d'entrée. -
Répéter les étapes 2 à 3 jusqu'à ce que l'activité se termine correctement.
Le flux de travail suivant met en œuvre la retry-until-success stratégie. L'interface de flux de travail est implémentée dans RetryActivityRecipeWorkflow et comporte une méthode, runUnreliableActivityTillSuccess, qui est le point d'entrée du flux de travail. L'exécuteur de flux de travail est implémenté dans RetryActivityRecipeWorkflowImpl, comme suit :
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(); } } }
Le flux de travail fonctionne comme suit :
-
runUnreliableActivityTillSuccesscrée un objetSettable<Boolean>nomméretryActivityqui est utilisé pour indiquer si l'activité a échoué et doit être réessayée.Settable<T>est dérivé dePromise<T>et fonctionne de la même manière, mais vous définissez une valeur de l'objetSettable<T>manuellement. -
runUnreliableActivityTillSuccessimplémente une classeTryCatchimbriquée anonyme pour traiter les exceptions qui sont déclenchées par l'activitéunreliableActivity. Pour en savoir plus sur le traitement des exceptions déclenchées par un code asynchrone, consultez Gestion des erreurs. -
doTryexécute l'activitéunreliableActivityqui renvoie un objetPromise<Void>nomméactivityRanSuccessfully. -
doTryappelle la méthodesetRetryActivityToFalseasynchrone et lui transmet deux paramètres :-
activityRanSuccessfullyprend l'objetPromise<Void>renvoyé par l'activitéunreliableActivity. -
retryActivityprend l'objetretryActivity.
Si
unreliableActivityse termine,activityRanSuccessfullydevient prêt etsetRetryActivityToFalsedéfinitretryActivitysur false. Sinon,activityRanSuccessfullyne devient jamais prêt etsetRetryActivityToFalsene s'exécute pas. -
-
Si
unreliableActivitydéclenche une exception, l'infrastructure appelledoCatchet lui transmet l'objet d'exception.doCatchdéfinitretryActivityavec la valeur true. -
runUnreliableActivityTillSuccessappelle la méthoderestartRunUnreliableActivityTillSuccessasynchrone et lui transmet l'objetretryActivity. CommeretryActivityest de typePromise<T>,restartRunUnreliableActivityTillSuccessdiffère l'exécution jusqu'à ce queretryActivitysoit prêt, ce qui a lieu une fois queTryCatchest terminé. -
Quand
retryActivityest prêt,restartRunUnreliableActivityTillSuccessextrait la valeur.-
Si la valeur est
false, la nouvelle tentative a réussi.restartRunUnreliableActivityTillSuccessne fait rien et la séquence de nouvelle tentative est arrêtée. -
Si la valeur est true, la nouvelle tentative a échoué.
restartRunUnreliableActivityTillSuccessappellerunUnreliableActivityTillSuccesspour exécuter l'activité à nouveau.
-
-
Le flux de travail répète les étapes 1 à 7 jusqu'à ce que
unreliableActivityse termine.
Note
doCatch ne traite pas l'exception ; il définit simplement l'objet retryActivity sur true pour indiquer que l'activité a échoué. La nouvelle tentative est traitée par la méthode restartRunUnreliableActivityTillSuccess asynchrone, ce qui diffère l'exécution jusqu'à ce que TryCatch se termine. La raison de cette approche est que si vous relancez une activité dans doCatch, vous ne pouvez pas l'annuler. La relance de l'activité dans restartRunUnreliableActivityTillSuccess vous permet d'exécuter des activités annulables.
Stratégie de nouvelle tentative exponentielle
Avec la stratégie de nouvelle tentative exponentielle, l'infrastructure exécute à nouveau une activité ayant échoué après une période de temps spécifiée, N secondes. Si cette tentative échoue, l'infrastructure exécute à nouveau l'activité après 2N secondes, puis après 4N secondes, et ainsi de suite. Comme le temps d'attente peut devenir très long, vous arrêtez généralement les nouvelles tentatives après un certain temps plutôt que de continuer indéfiniment.
L'infrastructure fournit trois façons d'implémenter une stratégie de nouvelle tentative exponentielle :
-
L'annotation
@ExponentialRetryest l'approche la plus simple, mais vous devez définir les options de configuration de nouvelle tentative lors de la compilation. -
La classe
RetryDecoratorvous permet de définir la configuration de nouvelle tentative lors de l'exécution et de la modifier si nécessaire. -
La classe
AsyncRetryingExecutorvous permet de définir la configuration de nouvelle tentative lors de l'exécution et de la modifier si nécessaire. En outre, l'infrastructure appelle une méthodeAsyncRunnable.runimplémentée par l'utilisateur pour exécuter chaque nouvelle tentative.
Toutes les approches prennent en charge les options de configuration suivantes, où les valeurs de temps sont exprimées en secondes :
-
Le temps d'attente initial avant une nouvelle tentative.
-
Le coefficient de recul qui est utilisé pour calculer les intervalles de nouvelle tentative, comme suit :
retryInterval = initialRetryIntervalSeconds * Math.pow(backoffCoefficient, numberOfTries - 2)La valeur par défaut est 2.0.
-
Le nombre maximum de nouvelles tentatives autorisées. La valeur par défaut est unlimited (illimité).
-
L'intervalle maximum de nouvelle tentative. La valeur par défaut est unlimited (illimité).
-
Le délai d'expiration. Les nouvelles tentatives s'arrêtent lorsque la durée totale du processus dépasse cette valeur. La valeur par défaut est unlimited (illimité).
-
Les exceptions qui déclenchent le processus de nouvelle tentative. Par défaut, toutes les exceptions déclenchent le processus de nouvelle tentative.
-
Les exceptions qui ne déclenchent pas le processus de nouvelle tentative. Par défaut, aucune exception n'est exclue.
Les sections suivantes décrivent les différentes façons d'implémenter une stratégie de nouvelle tentative exponentielle.
Réessayer de façon exponentielle avec @ ExponentialRetry
La façon la plus simple d'implémenter une stratégie de nouvelle tentative exponentielle pour une activité est d'appliquer une annotation @ExponentialRetry à l'activité dans la définition d'interface. Si l'activité échoue, l'infrastructure gère automatiquement le processus de nouvelle tentative en fonction des valeurs d'option spécifiées. Le modèle de base est le suivant :
-
Appliquer
@ExponentialRetryaux activités appropriées et spécifier la configuration de nouvelle tentative. -
Si une activité annotée échoue, l'infrastructure la relance automatiquement en fonction de la configuration spécifiée par les arguments de l'annotation.
L'exécuteur de flux de travail ExponentialRetryAnnotationWorkflow implémente la stratégie de nouvelle tentative exponentielle en utilisant une annotation @ExponentialRetry. Il utilise une activité unreliableActivity dont la définition d'interface est implémentée dans ExponentialRetryAnnotationActivities comme suit :
@Activities(version = "1.0") @ActivityRegistrationOptions( defaultTaskScheduleToStartTimeoutSeconds = 30, defaultTaskStartToCloseTimeoutSeconds = 30) public interface ExponentialRetryAnnotationActivities { @ExponentialRetry( initialRetryIntervalSeconds = 5, maximumAttempts = 5, exceptionsToRetry = IllegalStateException.class) public void unreliableActivity(); }
Les options @ExponentialRetry spécifient la stratégie suivante :
-
Effectuer une nouvelle tentative uniquement si l'activité déclenche
IllegalStateException. -
Utiliser un temps d'attente initial de 5 secondes.
-
Pas plus de 5 nouvelles tentatives.
L'interface de flux de travail est implémentée dans RetryWorkflow et comporte une méthode, process, qui est le point d'entrée du flux de travail. L'exécuteur de flux de travail est implémenté dans ExponentialRetryAnnotationWorkflowImpl, comme suit :
public class ExponentialRetryAnnotationWorkflowImpl implements RetryWorkflow { public void process() { handleUnreliableActivity(); } public void handleUnreliableActivity() { client.unreliableActivity(); } }
Le flux de travail fonctionne comme suit :
-
processexécute la méthodehandleUnreliableActivitysynchrone. -
handleUnreliableActivityexécute l'activitéunreliableActivity.
Si l'activité échoue en déclenchant IllegalStateException, l'infrastructure exécute automatiquement la stratégie de nouvelle tentative spécifiée dans ExponentialRetryAnnotationActivities.
Réessai exponentiel avec la classe RetryDecorator
@ExponentialRetry est simple à utiliser. Par contre, la configuration est statique et définie lors de la compilation. L'infrastructure utilise donc la même stratégie de nouvelle tentative chaque fois que l'activité échoue. Vous pouvez implémenter une stratégie de nouvelle tentative exponentielle plus flexible à l'aide de la classe RetryDecorator, qui vous permet de spécifier la configuration pendant l'exécution et de la modifier si nécessaire. Le modèle de base est le suivant :
-
Créer et configurer un objet
ExponentialRetryPolicyqui spécifie la configuration de nouvelle tentative. -
Créer un objet
RetryDecoratoret transmettre l'objetExponentialRetryPolicyde l'étape 1 au constructeur. -
Appliquer l'objet décorateur à l'activité en transmettant le nom de classe du client d'activité à la méthode de décoration de l'objet
RetryDecorator. -
Exécuter l'activité.
Si l'activité échoue, l'infrastructure la relance automatiquement en fonction de la configuration de l'objet ExponentialRetryPolicy. Vous pouvez modifier la configuration de nouvelle tentative si nécessaire en modifiant cet objet.
Note
L'annotation @ExponentialRetry et la classe RetryDecorator s'excluent mutuellement. Vous ne pouvez pas utiliser RetryDecorator pour remplacer dynamiquement une stratégie de nouvelle tentative spécifiée par une annotation @ExponentialRetry.
L'implémentation de flux de travail suivante montre comment utiliser la classe RetryDecorator pour implémenter une stratégie de nouvelle tentative exponentielle. Elle utilise une activité unreliableActivity qui ne comporte pas d'annotation @ExponentialRetry. L'interface de flux de travail est implémentée dans RetryWorkflow et comporte une méthode, process, qui est le point d'entrée du flux de travail. L'exécuteur de flux de travail est implémenté dans DecoratorRetryWorkflowImpl, comme suit :
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(); } }
Le flux de travail fonctionne comme suit :
-
processcrée et configure un objetExponentialRetryPolicyen :-
Transmettant l'intervalle de nouvelle tentative initial au constructeur.
-
Appel de la méthode
withMaximumAttemptsde l'objet pour définir le nombre maximal de tentatives sur 5.ExponentialRetryPolicyexpose d'autres objetswithque vous pouvez utiliser pour spécifier d'autres options de configuration.
-
-
processcrée un objetRetryDecoratornomméretryDecoratoret transmet l'objetExponentialRetryPolicyde l'étape 1 au constructeur. -
processapplique l'objet décorateur à l'activité en appelant la méthoderetryDecorator.decorateet en lui transmettant le nom de classe du client d'activité. -
handleUnreliableActivityexécute l'activité.
Si une activité échoue, l'infrastructure la relance en fonction de la configuration spécifiée à l'étape 1.
Note
Plusieurs des méthodes with de la classe ExponentialRetryPolicy ont une méthode set correspondante que vous pouvez appeler pour modifier l'option de configuration correspondante à tout moment : setBackoffCoefficient, setMaximumAttempts, setMaximumRetryIntervalSeconds et setMaximumRetryExpirationIntervalSeconds.
Réessai exponentiel avec la classe AsyncRetryingExecutor
La classe RetryDecorator offre plus de flexibilité pour la configuration du processus de nouvelle tentative que @ExponentialRetry, mais l'infrastructure exécute toujours les nouvelles tentatives automatiquement, en fonction de la configuration actuelle de l'objet ExponentialRetryPolicy. Une approche plus souple consiste à utiliser la classe AsyncRetryingExecutor. En plus de vous permettre de configurer le processus de nouvelle tentative pendant l'exécution, l'infrastructure appelle une méthode AsyncRunnable.run implémentée par l'utilisateur pour exécuter chaque nouvelle tentative au lieu de simplement exécuter l'activité.
Le modèle de base est le suivant :
-
Créer et configurer un objet
ExponentialRetryPolicypour spécifier la configuration de nouvelle tentative. -
Créer un objet
AsyncRetryingExecutor, et lui transmettre l'objetExponentialRetryPolicyet une instance de l'horloge de flux de travail. -
Implémenter une classe
TryCatchouTryCatchFinallyimbriquée anonyme. -
Implémenter une classe
AsyncRunnableanonyme et remplacer la méthoderunpour implémenter un code personnalisé afin d'exécuter l'activité. -
Remplacer
doTrypour appeler la méthodeexecutede l'objetAsyncRetryingExecutoret lui transmettre la classeAsyncRunnablede l'étape 4. L'objetAsyncRetryingExecutorappelleAsyncRunnable.runpour exécuter l'activité. -
Si l'activité échoue, l'objet
AsyncRetryingExecutorappelle à nouveau la méthodeAsyncRunnable.run, en fonction de la stratégie de nouvelle tentative spécifiée à l'étape 1.
Le flux de travail suivant montre comment utiliser la classe AsyncRetryingExecutor pour implémenter une stratégie de nouvelle tentative exponentielle. Il utilise la même activité unreliableActivity que le flux de travail DecoratorRetryWorkflow présenté précédemment. L'interface de flux de travail est implémentée dans RetryWorkflow et comporte une méthode, process, qui est le point d'entrée du flux de travail. L'exécuteur de flux de travail est implémenté dans AsyncExecutorRetryWorkflowImpl, comme suit :
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 { } }; } }
Le flux de travail fonctionne comme suit :
-
processappelle la méthodehandleUnreliableActivityet lui transmet les paramètres de configuration. -
handleUnreliableActivityutilise les paramètres de configuration de l'étape 1 pour créer un objetExponentialRetryPolicy,retryPolicy. -
handleUnreliableActivitycrée un objetAsyncRetryExecutor,executor, et transmet l'objetExponentialRetryPolicyde l'étape 2 et une instance de l'horloge de flux de travail au constructeur. -
handleUnreliableActivityimplémente une classeTryCatchimbriquée anonyme, et remplace les méthodesdoTryetdoCatchpour exécuter les nouvelles tentatives et traiter les exceptions. -
doTrycrée une classeAsyncRunnableanonyme et remplace la méthoderunpour implémenter un code personnalisé afin d'exécuterunreliableActivity. Pour des raisons de simplicité,runexécute seulement l'activité, mais vous pouvez implémenter des approches plus sophistiquées le cas échéant. -
doTryappelleexecutor.executeet transmet l'objetAsyncRunnable.executeappelle la méthoderunde l'objetAsyncRunnablepour exécuter l'activité. -
Si l'activité échoue, l'exécuteur appelle à nouveau
runen fonction de la configuration de l'objetretryPolicy.
Pour en savoir plus sur l'utilisation de la classe TryCatch pour gérer des erreurs, consultez AWS Flow Framework pour les exceptions Java.
Stratégie de nouvelle tentative personnalisée
L'approche la plus flexible pour réessayer les activités ayant échoué est une stratégie personnalisée, qui appelle de manière récursive une méthode asynchrone qui exécute la nouvelle tentative, un peu comme la stratégie. retry-until-success Par contre, au lieu de relancer simplement l'activité, vous implémentez une logique personnalisée qui décide si chaque nouvelle tentative successive doit être exécutée et de quelle façon. Le modèle de base est le suivant :
-
Créer un objet de statut
Settable<T>qui est utilisé pour indiquer si l'activité a échoué. -
Implémenter une classe
TryCatchouTryCatchFinallyimbriquée. -
doTryexécute l'activité. -
Si l'activité échoue,
doCatchdéfinit l'objet de statut pour indiquer que l'activité a échoué. -
Appeler une méthode de gestion des défaillances et lui transmettre l'objet de statut. La méthode diffère l'exécution jusqu'à ce que
TryCatchouTryCatchFinallysoit terminé. -
La méthode de gestion des défaillances décide s'il faut relancer l'activité, et si oui, quand.
Le flux de travail suivant montre comment implémenter une stratégie de nouvelle tentative personnalisée. Il utilise la même activité unreliableActivity que les flux de travail DecoratorRetryWorkflow et AsyncExecutorRetryWorkflow. L'interface de flux de travail est implémentée dans RetryWorkflow et comporte une méthode, process, qui est le point d'entrée du flux de travail. L'exécuteur de flux de travail est implémenté dans CustomLogicRetryWorkflowImpl, comme suit :
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; } }
Le flux de travail fonctionne comme suit :
-
processappelle la méthodecallActivityWithRetryasynchrone. -
callActivityWithRetrycrée un objetSettable<Throwable>nommé failure qui est utilisé pour indiquer si l'activité a échoué.Settable<T>est dérivé dePromise<T>et fonctionne de la même manière, mais vous définissez une valeur de l'objetSettable<T>manuellement. -
callActivityWithRetryimplémente une classeTryCatchFinallyimbriquée anonyme pour traiter les exceptions qui sont déclenchées parunreliableActivity. Pour en savoir plus sur le traitement des exceptions déclenchées par un code asynchrone, consultez AWS Flow Framework pour les exceptions Java. -
doTryexécuteunreliableActivity. -
Si
unreliableActivitylève une exception, le framework appelledoCatchet transmet l'objet d'exception.doCatchdéfinitfailuresur l'objet d'exception, ce qui indique que l'activité a échoué et place l'objet dans l'état prêt. -
doFinallyvérifie sifailureest prêt, ce qui est vrai seulement sifailurea été défini pardoCatch.-
S'il
failureest prêt, ildoFinallyne fait rien. -
Si
failuren'est pas prêt, l'activité est terminée etdoFinallydéfinit la défaillance (failure) surnull.
-
-
callActivityWithRetryappelle la méthoderetryOnFailureasynchrone et lui transmet « failure ». Comme « failure » est de typeSettable<T>,callActivityWithRetrydiffère l'exécution jusqu'à ce que « failure » soit prêt, ce qui a lieu une fois queTryCatchFinallyest terminé. -
retryOnFailureextrait la valeur de « failure ».-
Si l'objet failure est défini avec la valeur null, la nouvelle tentative est réussie.
retryOnFailurene fait rien, ce qui arrête le processus de nouvelle tentative. -
Si « failure » est défini sur un objet d'exception et que
shouldRetryrenvoie true,retryOnFailureappellecallActivityWithRetrypour relancer l'activité.shouldRetryimplémente une logique personnalisée qui décide s'il faut relancer une activité ayant échoué. Pour des raisons de simplicité,shouldRetryrenvoie toujourstrueetretryOnFailureexécute immédiatement l'activité, mais vous pouvez implémenter une logique plus sophistiquée le cas échéant.
-
-
Les étapes 2 à 8 se répètent jusqu'à ce que
unreliableActivityle processus soit terminé ou qu'il soitshouldRetrydécidé d'arrêter le processus.
Note
doCatch ne traite pas le processus de nouvelle tentative ; il définit simplement « failure » pour indiquer que l'activité a échoué. Le processus de nouvelle tentative est géré par la méthode retryOnFailure asynchrone, qui diffère l'exécution jusqu'à ce que TryCatch se termine. La raison de cette approche est que si vous relancez une activité dans doCatch, vous ne pouvez pas l'annuler. La relance de l'activité dans retryOnFailure vous permet d'exécuter des activités annulables.