Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Ripetere le attività non andate a buon fine
A volte le attività non vanno a buon fine per ragioni effimere, ad esempio una perdita temporanea della connessione. In altri casi l'attività va a buon fine, quindi il modo corretto di gestire l'errore è spesso quello di ripetere l'attività, anche più volte.
Esiste una serie di strategie per ripetere le attività; la migliore dipende dai dettagli del flusso di lavoro. Tali strategie rientrano in tre categorie di base:
-
La retry-until-success strategia continua semplicemente a riprovare l'attività fino al suo completamento.
-
La strategia di ripetizione esponenziale aumenta esponenzialmente l'intervallo di tempo tra i tentativi fino al completamento dell'attività o fino a quando il processo raggiunge un punto di arresto specifico, come un numero massimo di tentativi.
-
La strategia di ripetizione personalizzata decide se o come ripetere l'attività dopo ciascun tentativo non andato a buon fine.
Le sezioni seguenti descrivono come implementare queste strategie. I lavoratori del flusso di lavoro di esempio utilizzano tutti una singola attività, unreliableActivity, che esegue casualmente una delle seguenti operazioni:
-
Viene completata immediatamente
-
Non va a buon fine intenzionalmente superando il valore di timeout
-
Non va a buon fine intenzionalmente generando
IllegalStateException
Retry-Until-Success Strategia
La strategia più semplice è quella di ripetere l'attività ogni volta che ha esito negativo fino al buon esito. Il modello di base è:
-
Implementare una classe nidificata
TryCatchoTryCatchFinallynel metodo del punto di ingresso del flusso di lavoro. -
Eseguire l'attività in
doTry. -
Se l'attività non va a buon fine, il framework chiama
doCatch, che esegue nuovamente il metodo del punto di ingresso. -
Ripetere le fasi 2 e 3 fino al completamento con esito positivo dell'attività.
Il seguente flusso di lavoro implementa la retry-until-success strategia. L'interfaccia del flusso di lavoro è implementata in RetryActivityRecipeWorkflow e ha un metodo, runUnreliableActivityTillSuccess, che è il punto di ingresso del flusso di lavoro. Il lavoratore del flusso di lavoro viene implementato in RetryActivityRecipeWorkflowImpl, nel seguente modo:
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(); } } }
Il flusso di lavoro funziona come segue:
-
runUnreliableActivityTillSuccesscrea un oggettoSettable<Boolean>denominatoretryActivityche viene usato per indicare se l'attività non è riuscita e deve essere ritentata.Settable<T>è derivato daPromise<T>e funziona allo stesso modo ma il valore dell'oggettoSettable<T>viene impostato manualmente. -
runUnreliableActivityTillSuccessimplementa una classe annidata anonimaTryCatchper gestire le eccezioni generate dall'attivitàunreliableActivity. Per ulteriori discussioni su come gestire le eccezioni generate da un codice asincrono, consulta Gestione errori. -
doTryesegue l'attivitàunreliableActivity, che restituisce un oggettoPromise<Void>di nomeactivityRanSuccessfully. -
doTrychiama il metodo asincronosetRetryActivityToFalse, che ha due parametri:-
activityRanSuccessfullyaccetta l'oggettoPromise<Void>restituito dall'attivitàunreliableActivity. -
retryActivityaccetta l'oggettoretryActivity.
Se
unreliableActivityviene completato,activityRanSuccessfullydiventa pronto esetRetryActivityToFalseimpostaretryActivitysu false. In caso contrario,activityRanSuccessfullynon diventa mai pronto esetRetryActivityToFalsenon viene eseguito. -
-
Se
unreliableActivitygenera un'eccezione, il framework chiamadoCatche lo trasferisce all'oggetto dell'eccezione.doCatchimpostaretryActivitysu true. -
runUnreliableActivityTillSuccesschiama il metodo asincronorestartRunUnreliableActivityTillSuccesse lo trasferisce all'oggettoretryActivity. PoichéretryActivityè un tipoPromise<T>,restartRunUnreliableActivityTillSuccessritarda l'esecuzione fin quandoretryActivityè pronto, il che si verifica dopo il completamento diTryCatch. -
Quando
retryActivityè pronto,restartRunUnreliableActivityTillSuccessestrae il valore.-
Se il valore è
false, il nuovo tentativo è andato a buon fine.restartRunUnreliableActivityTillSuccessnon è operativo e la sequenza di ripetizione termina. -
Se il valore è true, il nuovo tentativo non è andato a buon fine.
restartRunUnreliableActivityTillSuccesschiamarunUnreliableActivityTillSuccessper eseguire nuovamente l'attività.
-
-
Si ripetono le fasi 1-7 fino al completamento di
unreliableActivity.
Nota
doCatch non gestisce l'eccezione; imposta semplicemente l'oggetto retryActivity su true per indicare l'esito negativo dell'attività. La ripetizione è gestita dal metodo asincrono restartRunUnreliableActivityTillSuccess, che ritarda l'esecuzione fino al completamento di TryCatch. Il motivo di questo approccio è che se riprovi un'attività in doCatch non puoi annullarla. Ripetere l'attività in restartRunUnreliableActivityTillSuccess ti permette di eseguire attività annullabili.
Strategia di ripetizione esponenziale
Con la strategia di ripetizione esponenziale, il framework esegue nuovamente un'attività non andata a buon fine dopo un periodo di tempo specifico, N secondi. Se il tentativo ha esito negativo, il framework esegue nuovamente l'attività dopo 2N secondi, 4N secondi e così via. Poiché il tempo di attesa può essere lungo, in genere i tentativi si arrestano a un certo punto invece che proseguire all'infinito.
Il framework prevede tre modi per implementare una strategia di ripetizione esponenziale:
-
L'annotazione
@ExponentialRetryè l'approccio più semplice, ma devi impostare le opzioni di configurazione della ripetizione al momento della compilazione. -
La classe
RetryDecoratorti permette di impostare la configurazione della ripetizione in fase di runtime e di modificarla in base alle necessità. -
La classe
AsyncRetryingExecutorti permette di impostare la configurazione della ripetizione in fase di runtime e di modificarla in base alle necessità. Inoltre, il framework chiama un metodoAsyncRunnable.runimplementato dall'utente per eseguire ogni tentativo di ripetizione.
Tutti gli approcci supportano le seguenti opzioni di configurazione, in cui i valori di tempo sono espressi in secondi:
-
Il tempo di attesa per la ripetizione iniziale.
-
Il coefficiente di backoff, che viene utilizzato per calcolare gli intervalli di ripetizione, nel modo seguente:
retryInterval = initialRetryIntervalSeconds * Math.pow(backoffCoefficient, numberOfTries - 2)Il valore predefinito è 2.0.
-
Il numero massimo di tentativi di ripetizione. Il valore predefinito è illimitato.
-
L'intervallo massimo di ripetizione. Il valore predefinito è illimitato.
-
Il tempo di scadenza. I tentativi si arrestano quando la durata totale del processo supera questo valore. Il valore predefinito è illimitato.
-
Le eccezioni che attivano il processo di ripetizione. Per impostazione predefinita, tutte le eccezioni attivano il processo di ripetizione.
-
Le eccezioni che non attivano tentativi di ripetizione. Per impostazione predefinita, non è esclusa alcuna eccezione.
Le sezioni seguenti descrivono i vari modi in cui è possibile implementare una strategia di ripetizione esponenziale.
Riprova esponenziale con @ ExponentialRetry
Il modo più semplice per implementare una strategia di ripetizione esponenziale per un'attività è applicare un'annotazione @ExponentialRetry all'attività nella definizione dell'interfaccia. Se l'attività non va a buon fine, il framework gestisce automaticamente il processo di ripetizione in base ai valori opzionali specificati. Il modello di base è:
-
Applica
@ExponentialRetryalle attività in questione e specifica la configurazione di ripetizione. -
Se l'attività annotata non va a buon fine, il framework la recupera automaticamente secondo la configurazione specificata dagli argomenti dell'annotazione.
Il lavoratore del flusso di lavoro ExponentialRetryAnnotationWorkflow implementa la strategia di ripetizione esponenziale utilizzando un'annotazione @ExponentialRetry. Utilizza un'attività unreliableActivity la cui definizione dell'interfaccia è implementata in ExponentialRetryAnnotationActivities, nel modo seguente:
@Activities(version = "1.0") @ActivityRegistrationOptions( defaultTaskScheduleToStartTimeoutSeconds = 30, defaultTaskStartToCloseTimeoutSeconds = 30) public interface ExponentialRetryAnnotationActivities { @ExponentialRetry( initialRetryIntervalSeconds = 5, maximumAttempts = 5, exceptionsToRetry = IllegalStateException.class) public void unreliableActivity(); }
Le opzioni di @ExponentialRetry specificano la seguente strategia:
-
Ripeti sono se l'attività genera
IllegalStateException. -
Utilizza un tempo di attesa iniziale di 5 secondi.
-
Non più di 5 tentativi di ripetizione.
L'interfaccia del flusso di lavoro è implementata in RetryWorkflow e ha un metodo, process, che è il punto di ingresso del flusso di lavoro. Il lavoratore del flusso di lavoro viene implementato in ExponentialRetryAnnotationWorkflowImpl, nel seguente modo:
public class ExponentialRetryAnnotationWorkflowImpl implements RetryWorkflow { public void process() { handleUnreliableActivity(); } public void handleUnreliableActivity() { client.unreliableActivity(); } }
Il flusso di lavoro funziona come segue:
-
processesegue il metodo asincronohandleUnreliableActivity. -
handleUnreliableActivityesegue l'attivitàunreliableActivity.
Se l'attività non va a buon fine generando IllegalStateException, il framework esegue automaticamente la strategia di ripetizione specificata in ExponentialRetryAnnotationActivities.
Riprova esponenziale con la classe RetryDecorator
@ExponentialRetry è semplice da usare. Tuttavia, la configurazione è statica e impostata al momento della compilazione, in modo che il framework utilizzi la stessa strategia di ripetizione ogni volta che l'attività non va a buon fine. Puoi implementare una strategia di ripetizione esponenziale più flessibile utilizzando la classe RetryDecorator, che ti permette di specificare la configurazione in fase di runtime e di modificarla in base alle necessità. Il modello di base è:
-
Crea e configura un oggetto
ExponentialRetryPolicyche specifichi la configurazione della ripetizione. -
Crea un oggetto
RetryDecoratore trasferisci l'oggettoExponentialRetryPolicydella Fase 1 al costruttore. -
Applica l'oggetto decorator all'attività trasferendo il nome della classe del client di attività al metodo decorato dell'oggetto
RetryDecorator. -
Esegui l'attività.
Se l'attività non va a buon fine, il framework la ripete secondo la configurazione dell'oggetto ExponentialRetryPolicy. Puoi modificare la configurazione della ripetizione in base alla necessità cambiando l'oggetto.
Nota
L'annotazione @ExponentialRetry e la classe RetryDecorator sono reciprocamente esclusive. Non puoi utilizzare RetryDecorator per sovrascrivere dinamicamente una policy di ripetizione specificata da un'annotazione @ExponentialRetry.
La seguente implementazione del flusso di lavoro mostra come utilizzare la classe RetryDecorator per implementare una strategia di ripetizione esponenziale. Utilizza un'attività unreliableActivity priva dell'annotazione @ExponentialRetry. L'interfaccia del flusso di lavoro è implementata in RetryWorkflow e ha un metodo, process, che è il punto di ingresso del flusso di lavoro. Il lavoratore del flusso di lavoro viene implementato in DecoratorRetryWorkflowImpl, nel seguente modo:
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(); } }
Il flusso di lavoro funziona come segue:
-
processcrea e configura un oggettoExponentialRetryPolicynel seguente modo:-
Trasferendo al costruttore l'intervallo di ripetizione iniziale.
-
Chiamando il metodo
withMaximumAttemptsdell'oggetto per impostare il numero massimo di tentativi a 5.ExponentialRetryPolicyespone altri oggettiwithche è possibile usare per specificare altre opzioni di configurazione.
-
-
processcrea un oggettoRetryDecoratorcon nomeretryDecoratore trasferisce l'oggettoExponentialRetryPolicydella Fase 1 al costruttore. -
processapplica l'elemento decorator all'attività chiamando il metodoretryDecorator.decoratee trasferendolo al nome della classe del client di attività. -
handleUnreliableActivityesegue l'attività.
Se l'attività non va a buon fine, il framework la ripete secondo la configurazione specificata nella Fase 1.
Nota
Molti dei metodi with della classe ExponentialRetryPolicy hanno un metodo corrispondente set che puoi chiamare per modificare l'opzione di configurazione corrispondente in qualsiasi momento: setBackoffCoefficient, setMaximumAttempts, setMaximumRetryIntervalSeconds e setMaximumRetryExpirationIntervalSeconds.
Riprova esponenziale con la classe AsyncRetryingExecutor
La classe RetryDecorator offre più flessibilità nella configurazione del processo di ripetizione rispetto a @ExponentialRetry, ma il framework esegue comunque automaticamente i tentativi di ripetizione, in base alla attuale configurazione dell'oggetto ExponentialRetryPolicy. Un approccio più flessibile prevede l'utilizzo della classe AsyncRetryingExecutor. Oltre a permetterti di configurare il processo di ripetizione in fase di runtime, il framework chiama un metodo AsyncRunnable.run implementato dall'utente per eseguire ogni tentativo di ripetizione invece che eseguire semplicemente l'attività.
Il modello di base è:
-
Crea e configura un oggetto
ExponentialRetryPolicyper specificare la configurazione della ripetizione. -
Crea un oggetto
AsyncRetryingExecutore trasferiscigli l'oggettoExponentialRetryPolicye un'istanza dell'orologio del flusso di lavoro. -
Implementa una classe annidata anonima
TryCatchoTryCatchFinally. -
Implementa una classe anonima
AsyncRunnablee sovrascrivi il metodorunper implementare il codice personalizzato per eseguire l'attività. -
Sovrascrivi
doTryper chiamare il metodoexecutedell'oggettoAsyncRetryingExecutore trasferirlo alla classeAsyncRunnabledalla fase 4. L'oggettoAsyncRetryingExecutorchiamaAsyncRunnable.runper eseguire l'attività. -
Se l'attività non va a buon fine, l'oggetto
AsyncRetryingExecutorchiama nuovamente il metodoAsyncRunnable.runsecondo la policy di ripetizione specificata nella Fase 1.
Il flusso di lavoro seguente mostra come utilizzare la classe AsyncRetryingExecutor per implementare una strategia di ripetizione esponenziale. Utilizza la stessa attività unreliableActivity del flusso di lavoro DecoratorRetryWorkflow discusso in precedenza. L'interfaccia del flusso di lavoro è implementata in RetryWorkflow e ha un metodo, process, che è il punto di ingresso del flusso di lavoro. Il lavoratore del flusso di lavoro viene implementato in AsyncExecutorRetryWorkflowImpl, nel seguente modo:
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 { } }; } }
Il flusso di lavoro funziona come segue:
-
processchiama il metodohandleUnreliableActivitye lo trasferisce alle impostazioni della configurazione. -
handleUnreliableActivityutilizza le impostazioni di configurazione della Fase 1 per creare un oggettoExponentialRetryPolicy, l'oggettoretryPolicy. -
handleUnreliableActivitycrea un oggettoAsyncRetryExecutor,executore trasferisce l'oggettoExponentialRetryPolicydella Fase 2 e un'istanza dell'orologio del flusso di lavoro al costruttore -
handleUnreliableActivityimplementa una classe annidata anonimaTryCatche sovrascrive i metodidoTryedoCatchper eseguire i tentativi di ripetizione e gestire le eventuali eccezioni. -
doTrycrea una classe anonimaAsyncRunnablee sovrascrive il metodorunper implementare il codice personalizzato per eseguireunreliableActivity. Per semplicità,runesegue semplicemente l'attività, ma puoi implementare approcci più sofisticati in base alle necessità. -
doTrychiamaexecutor.executee lo trasferisce all'oggettoAsyncRunnable.executechiama il metodoAsyncRunnabledell'oggettorunper eseguire l'attività. -
Se l'attività non va a buon fine, l'esecutore chiama di nuovo
runin base alla configurazione dell'oggettoretryPolicy.
Per ulteriori discussioni su come utilizzare la classe TryCatch per gestire gli errori, consulta AWS Flow Framework per le eccezioni Java.
Strategia di ripetizione personalizzata
L'approccio più flessibile per riprovare le attività non riuscite è una strategia personalizzata, che richiama ricorsivamente un metodo asincrono che esegue il tentativo di nuovo tentativo, proprio come la strategia. retry-until-success Tuttavia, invece che rieseguire semplicemente l'attività, implementi una logica personalizzata che decide se e come eseguire i successivi tentativi di ripetizione. Il modello di base è:
-
Crea un oggetto di stato
Settable<T>, che viene utilizzato per indicare se l'attività non è andata a buon fine. -
Implementa una classe annidata
TryCatchoTryCatchFinally. -
doTryesegue l'attività. -
Se l'attività non va a buon fine,
doCatchimposta l'oggetto di stato per indicare che l'attività ha avuto esito negativo. -
Chiama un metodo asincrono di gestione dell'errore e trasferiscilo all'oggetto di stato. Il metodo ritarda l'esecuzione fino al completamento di
TryCatchoTryCatchFinally. -
Il metodo di gestione dell'errore decide se e quando ripetere l'attività.
Il flusso di lavoro seguente mostra come implementare una strategia di ripetizione personalizzata. Utilizza la stessa attività unreliableActivity dei flussi di lavoro DecoratorRetryWorkflow e AsyncExecutorRetryWorkflow. L'interfaccia del flusso di lavoro è implementata in RetryWorkflow e ha un metodo, process, che è il punto di ingresso del flusso di lavoro. Il lavoratore del flusso di lavoro viene implementato in CustomLogicRetryWorkflowImpl, nel seguente modo:
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; } }
Il flusso di lavoro funziona come segue:
-
processchiama il metodo asincronocallActivityWithRetry. -
callActivityWithRetrycrea un errore di oggettoSettable<Throwable>denominato che viene utilizzato per indicare se l'attività non è andata a buon fine.Settable<T>deriva daPromise<T>e funziona quasi allo stesso modo, ma il valore dell'oggettoSettable<T>è impostato manualmente. -
callActivityWithRetryimplementa una classe annidata anonimaTryCatchFinallyper gestire le eccezioni generate daunreliableActivity. Per ulteriori discussioni su come gestire le eccezioni generate da un codice asincrono, consulta AWS Flow Framework per le eccezioni Java. -
doTryesegueunreliableActivity. -
Se
unreliableActivitygenera un'eccezione, il framework chiamadoCatche lo trasferisce all'oggetto dell'eccezione.doCatchimpostafailuresull'oggetto dell'eccezione, il che indica che l'attività non è andata a buon fine e mette l'oggetto in stato di pronto. -
doFinallyverifica sefailureè pronto, che sarà vero solo sefailureè stato impostato dadoCatch.-
Se è pronto, non fa nulla.
failuredoFinally -
Se
failurenon è pronto, l'attività viene completata edoFinallyimposta l'errore sunull.
-
-
callActivityWithRetrychiama il metodo asincronoretryOnFailuree vi trasferisce l'errore. Poiché l'errore è un tipoSettable<T>,callActivityWithRetryritarda l'esecuzione fin quando l'errore è pronto, il che si verifica dopo il completamento diTryCatchFinally. -
retryOnFailurericeve il valore dall'errore.-
Se l'errore è impostato su null, il tentativo di ripetizione è andato a buon fine.
retryOnFailurenon fa alcunché, il che termina il processo di ripetizione. -
Se l'errore è impostato su un oggetto di eccezione e
shouldRetryrestituisce il valore true,retryOnFailurechiamacallActivityWithRetryper riprovare l'attività.shouldRetryimplementa una logica personalizzata per decidere se ripetere un'attività dall'esito negativo. Per semplicità,shouldRetryrestituisce sempre il valoretrueeretryOnFailureesegue immediatamente l'attività, ma puoi implementare una logica più sofisticata in base alle necessità.
-
-
I passaggi da 2 a 8 si ripetono fino al
unreliableActivitycompletamento o allashouldRetrydecisione di interrompere il processo.
Nota
doCatch non gestisce il processo di ripetizione; imposta semplicemente l'errore per indicare l'esito negativo dell'attività. Il processo di ripetizione è gestito dal metodo asincrono retryOnFailure, che ritarda l'esecuzione fino al completamento di TryCatch. Il motivo di questo approccio è che se riprovi un'attività in doCatch non puoi annullarla. Ripetere l'attività in retryOnFailure ti permette di eseguire attività annullabili.