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 strategia di ripetizione fino al buon esito continua semplicemente a ripetere 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
Strategia di ripetizione fino al buon esito
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
TryCatch
oTryCatchFinally
nel 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 flusso di lavoro seguente implementa la strategia di ripetizione fino all'esito positivo. 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:
-
runUnreliableActivityTillSuccess
crea un oggettoSettable<Boolean>
denominatoretryActivity
che 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. -
runUnreliableActivityTillSuccess
implementa una classe annidata anonimaTryCatch
per gestire le eccezioni generate dall'attivitàunreliableActivity
. Per ulteriori discussioni su come gestire le eccezioni generate da un codice asincrono, consulta Gestione errori. -
doTry
esegue l'attivitàunreliableActivity
, che restituisce un oggettoPromise<Void>
di nomeactivityRanSuccessfully
. -
doTry
chiama il metodo asincronosetRetryActivityToFalse
, che ha due parametri:-
activityRanSuccessfully
accetta l'oggettoPromise<Void>
restituito dall'attivitàunreliableActivity
. -
retryActivity
accetta l'oggettoretryActivity
.
Se
unreliableActivity
viene completato,activityRanSuccessfully
diventa pronto esetRetryActivityToFalse
impostaretryActivity
su false. In caso contrario,activityRanSuccessfully
non diventa mai pronto esetRetryActivityToFalse
non viene eseguito. -
-
Se
unreliableActivity
genera un'eccezione, il framework chiamadoCatch
e lo trasferisce all'oggetto dell'eccezione.doCatch
impostaretryActivity
su true. -
runUnreliableActivityTillSuccess
chiama il metodo asincronorestartRunUnreliableActivityTillSuccess
e lo trasferisce all'oggettoretryActivity
. PoichéretryActivity
è un tipoPromise<T>
,restartRunUnreliableActivityTillSuccess
ritarda l'esecuzione fin quandoretryActivity
è pronto, il che si verifica dopo il completamento diTryCatch
. -
Quando
retryActivity
è pronto,restartRunUnreliableActivityTillSuccess
estrae il valore.-
Se il valore è
false
, il nuovo tentativo è andato a buon fine.restartRunUnreliableActivityTillSuccess
non è operativo e la sequenza di ripetizione termina. -
Se il valore è true, il nuovo tentativo non è andato a buon fine.
restartRunUnreliableActivityTillSuccess
chiamarunUnreliableActivityTillSuccess
per 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
RetryDecorator
ti permette di impostare la configurazione della ripetizione in fase di runtime e di modificarla in base alle necessità. -
La classe
AsyncRetryingExecutor
ti permette di impostare la configurazione della ripetizione in fase di runtime e di modificarla in base alle necessità. Inoltre, il framework chiama un metodoAsyncRunnable.run
implementato 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.
Ripetizione 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
@ExponentialRetry
alle 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:
-
process
esegue il metodo asincronohandleUnreliableActivity
. -
handleUnreliableActivity
esegue l'attivitàunreliableActivity
.
Se l'attività non va a buon fine generando IllegalStateException
, il framework esegue automaticamente la strategia di ripetizione specificata in ExponentialRetryAnnotationActivities
.
Ripetizione 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
ExponentialRetryPolicy
che specifichi la configurazione della ripetizione. -
Crea un oggetto
RetryDecorator
e trasferisci l'oggettoExponentialRetryPolicy
della 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:
-
process
crea e configura un oggettoExponentialRetryPolicy
nel seguente modo:-
Trasferendo al costruttore l'intervallo di ripetizione iniziale.
-
Chiamando il metodo
withMaximumAttempts
dell'oggetto per impostare il numero massimo di tentativi a 5.ExponentialRetryPolicy
espone altri oggettiwith
che è possibile usare per specificare altre opzioni di configurazione.
-
-
process
crea un oggettoRetryDecorator
con nomeretryDecorator
e trasferisce l'oggettoExponentialRetryPolicy
della Fase 1 al costruttore. -
process
applica l'elemento decorator all'attività chiamando il metodoretryDecorator.decorate
e trasferendolo al nome della classe del client di attività. -
handleUnreliableActivity
esegue 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
.
Ripetizione 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
ExponentialRetryPolicy
per specificare la configurazione della ripetizione. -
Crea un oggetto
AsyncRetryingExecutor
e trasferiscigli l'oggettoExponentialRetryPolicy
e un'istanza dell'orologio del flusso di lavoro. -
Implementa una classe annidata anonima
TryCatch
oTryCatchFinally
. -
Implementa una classe anonima
AsyncRunnable
e sovrascrivi il metodorun
per implementare il codice personalizzato per eseguire l'attività. -
Sovrascrivi
doTry
per chiamare il metodoexecute
dell'oggettoAsyncRetryingExecutor
e trasferirlo alla classeAsyncRunnable
dalla fase 4. L'oggettoAsyncRetryingExecutor
chiamaAsyncRunnable.run
per eseguire l'attività. -
Se l'attività non va a buon fine, l'oggetto
AsyncRetryingExecutor
chiama nuovamente il metodoAsyncRunnable.run
secondo 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:
-
process
chiama il metodohandleUnreliableActivity
e lo trasferisce alle impostazioni della configurazione. -
handleUnreliableActivity
utilizza le impostazioni di configurazione della Fase 1 per creare un oggettoExponentialRetryPolicy
, l'oggettoretryPolicy
. -
handleUnreliableActivity
crea un oggettoAsyncRetryExecutor
,executor
e trasferisce l'oggettoExponentialRetryPolicy
della Fase 2 e un'istanza dell'orologio del flusso di lavoro al costruttore -
handleUnreliableActivity
implementa una classe annidata anonimaTryCatch
e sovrascrive i metodidoTry
edoCatch
per eseguire i tentativi di ripetizione e gestire le eventuali eccezioni. -
doTry
crea una classe anonimaAsyncRunnable
e sovrascrive il metodorun
per implementare il codice personalizzato per eseguireunreliableActivity
. Per semplicità,run
esegue semplicemente l'attività, ma puoi implementare approcci più sofisticati in base alle necessità. -
doTry
chiamaexecutor.execute
e lo trasferisce all'oggettoAsyncRunnable
.execute
chiama il metodoAsyncRunnable
dell'oggettorun
per eseguire l'attività. -
Se l'attività non va a buon fine, l'esecutore chiama di nuovo
run
in base alla configurazione dell'oggettoretryPolicy
.
Per ulteriori discussioni su come utilizzare la classe TryCatch
per gestire gli errori, consulta AWS Flow Frameworkper le eccezioni Java.
Strategia di ripetizione personalizzata
L'approccio più flessibile per ripetere le attività non andate a buon fine è una strategia personalizzata, che chiama ricorsivamente un metodo asincrono che esegue il tentativo di ripetizione, più o meno come la strategia di ripetizione fino all'esito positivo. 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
TryCatch
oTryCatchFinally
. -
doTry
esegue l'attività. -
Se l'attività non va a buon fine,
doCatch
imposta 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
TryCatch
oTryCatchFinally
. -
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:
-
process
chiama il metodo asincronocallActivityWithRetry
. -
callActivityWithRetry
crea 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. -
callActivityWithRetry
implementa una classe annidata anonimaTryCatchFinally
per gestire le eccezioni generate daunreliableActivity
. Per ulteriori discussioni su come gestire le eccezioni generate da un codice asincrono, consulta AWS Flow Frameworkper le eccezioni Java. -
doTry
esegueunreliableActivity
. -
Se
unreliableActivity
genera un'eccezione, il framework chiamadoCatch
e lo trasferisce all'oggetto dell'eccezione.doCatch
impostafailure
sull'oggetto dell'eccezione, il che indica che l'attività non è andata a buon fine e mette l'oggetto in stato di pronto. -
doFinally
verifica sefailure
è pronto, che sarà vero solo sefailure
è stato impostato dadoCatch
.-
Se
failure
è pronto,doFinally
non fa alcunché. -
Se
failure
non è pronto, l'attività viene completata edoFinally
imposta l'errore sunull
.
-
-
callActivityWithRetry
chiama il metodo asincronoretryOnFailure
e vi trasferisce l'errore. Poiché l'errore è un tipoSettable<T>
,callActivityWithRetry
ritarda l'esecuzione fin quando l'errore è pronto, il che si verifica dopo il completamento diTryCatchFinally
. -
retryOnFailure
riceve il valore dall'errore.-
Se l'errore è impostato su null, il tentativo di ripetizione è andato a buon fine.
retryOnFailure
non fa alcunché, il che termina il processo di ripetizione. -
Se l'errore è impostato su un oggetto di eccezione e
shouldRetry
restituisce il valore true,retryOnFailure
chiamacallActivityWithRetry
per riprovare l'attività.shouldRetry
implementa una logica personalizzata per decidere se ripetere un'attività dall'esito negativo. Per semplicità,shouldRetry
restituisce sempre il valoretrue
eretryOnFailure
esegue immediatamente l'attività, ma puoi implementare una logica più sofisticata in base alle necessità.
-
-
I passaggi 2—8 ripetono fino a quando
unreliableActivity
completa oshouldRetry
decide 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.