As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.
Tentar novamente atividades com falha
As atividades falham às vezes por motivos efêmeros, como uma perda temporária de conectividade. Em outro momento, a atividade pode funcionar, assim a forma apropriada de lidar com falhas de atividade geralmente é repetir a atividade, talvez algumas vezes.
Existe uma variedade de estratégias para repetir atividades. A melhor depende dos detalhes do fluxo de trabalho. As estratégias são divididas em três categorias básicas:
-
A estratégia de repetir até o sucesso simplesmente repete a atividade até ela ser concluída.
-
A estratégia de repetição exponencial aumenta o intervalo de tempo entre as tentativas exponencialmente até que a atividade seja concluída ou o processo alcançar um ponto de parada especificado, como um número máximo de tentativas.
-
A estratégia de repetição personalizada decide se ou como repetir a atividade após cada tentativa falha.
As seções a seguir descrevem como implementar essas estratégias. Todos os operadores de fluxo de trabalho de exemplo utilizam uma única atividade, unreliableActivity
, que realiza de forma aleatória um dos seguintes:
-
Conclui imediatamente
-
Falha intencionalmente ao exceder o valor do tempo limite
-
Falha intencionalmente lançando uma
IllegalStateException
Estratégia de repetir até o sucesso
A estratégia mais simples de repetição é continuar repetindo a atividade até que eventualmente seja concluída. O padrão básico é:
-
Implementar uma classe aninhada
TryCatch
ouTryCatchFinally
no método do ponto de entrada do fluxo de trabalho. -
Executar a atividade em
doTry
-
Se a atividade falhar, a estrutura chama
doCatch
, que executa o método do ponto de entrada novamente. -
Repetir as Etapas 2 e 3 até que a atividade seja concluída com êxito.
O fluxo de trabalho a seguir implementa a estratégia de repetir até o sucesso. A interface do fluxo de trabalho é implementada no RetryActivityRecipeWorkflow
e possui um método, runUnreliableActivityTillSuccess
, que é o ponto de entrada do fluxo de trabalho. O operador do fluxo de trabalho é implementado em RetryActivityRecipeWorkflowImpl
, da seguinte maneira:
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(); } } }
O fluxo de trabalho funciona da seguinte forma:
-
runUnreliableActivityTillSuccess
cria um objetoSettable<Boolean>
chamadoretryActivity
que é usado para indicar se a atividade falhou e deve ser executada novamente.Settable<T>
é derivado doPromise<T>
e funciona da mesma forma, mas você define o valor de um objetoSettable<T>
manualmente. -
runUnreliableActivityTillSuccess
implementa uma classe aninhadaTryCatch
anônima para processar todas as exceções lançadas pela atividadeunreliableActivity
. Para obter mais discussões sobre como gerenciar as exceções lançadas pelo código assíncrono, consulte Como tratar erros. -
doTry
executa a atividadeunreliableActivity
, que retorna um objetoPromise<Void>
chamadoactivityRanSuccessfully
. -
doTry
chama o método assíncronosetRetryActivityToFalse
, que possui dois parâmetros:-
activityRanSuccessfully
recebe o objetoPromise<Void>
retornado pela atividadeunreliableActivity
. -
retryActivity
recebe o objetoretryActivity
.
Se a
unreliableActivity
for concluída,activityRanSuccessfully
se torna pronto esetRetryActivityToFalse
defineretryActivity
como falso. Caso contrário,activityRanSuccessfully
nunca se torna pronto esetRetryActivityToFalse
não é executado. -
-
Se a
unreliableActivity
lança uma exceção, a estrutura chamadoCatch
e envia-lhe o objeto de exceção.doCatch
defineretryActivity
como verdadeiro. -
runUnreliableActivityTillSuccess
chama o método assíncronorestartRunUnreliableActivityTillSuccess
e envia-lhe o objetoretryActivity
. ComoretryActivity
é do tipoPromise<T>
,restartRunUnreliableActivityTillSuccess
adia a execução até que aretryActivity
esteja pronta, o que ocorre apósTryCatch
concluir. -
Quando a
retryActivity
estiver pronta,restartRunUnreliableActivityTillSuccess
extrai o valor.-
Se o valor for
false
, a nova tentativa foi bem-sucedida.restartRunUnreliableActivityTillSuccess
não faz nada e a sequência de repetição é encerrada. -
Se o valor for verdadeiro, a nova tentativa falhou.
restartRunUnreliableActivityTillSuccess
chamarunUnreliableActivityTillSuccess
para executar a atividade novamente.
-
-
As Etapas 1 a 7 são repetias até que a
unreliableActivity
seja concluída.
nota
doCatch
não lida com a exceção, ele simplesmente define o objeto retryActivity
como verdadeiro para indicar que a atividade falhou. A repetição é gerenciada pelo método assíncrono restartRunUnreliableActivityTillSuccess
, que adia a execução até que TryCatch
seja concluído. O motivo desta abordagem é que, se você repetir uma atividade no doCatch
, não é possível cancelá-la. Repetir a atividade em restartRunUnreliableActivityTillSuccess
permite executar atividades canceláveis.
Estratégia de repetição exponencial
Com a estratégia de repetição exponencial, a estrutura executa uma atividade falha novamente após um período especificado, N segundos. Se essa tentativa falhar, a estrutura executa a atividade novamente após 2N segundos e, em seguida, 4N segundos e assim por diante. Como o tempo de espera pode se tornar grande, normalmente você interrompe as tentativas de repetição em algum momento em vez de continuar indefinidamente.
A estrutura oferece três maneiras para implementar uma estratégia de repetição exponencial:
-
A anotação
@ExponentialRetry
é a abordagem mais simples, mas você deve definir as opções de configuração da repetição no momento da compilação. -
A classe
RetryDecorator
permite definir a configuração de repetição durante o tempo de execução e alterá-la conforme necessário. -
A classe
AsyncRetryingExecutor
permite definir a configuração de repetição durante o tempo de execução e alterá-la conforme necessário. Além disso, a estrutura chama um métodoAsyncRunnable.run
implementado pelo usuário para executar cada tentativa de repetição.
Todas as abordagens oferecem suporte para as seguintes opções de configuração, onde os valores de tempo estão em segundos:
-
O tempo de espera da repetição inicial.
-
O coeficiente de recuo, que é usado para computar os intervalos de repetição, da seguinte forma:
retryInterval = initialRetryIntervalSeconds * Math.pow(backoffCoefficient, numberOfTries - 2)
O valor padrão é 2.0.
-
O número máximo de tentativas de repetição. O valor padrão é ilimitado.
-
O intervalo máximo de repetição. O valor padrão é ilimitado.
-
O tempo de expiração. As tentativas de repetição param quando a duração total do processo excede esse valor. O valor padrão é ilimitado.
-
As exceções que acionarão o processo de repetição. Por padrão, toda exceção aciona o processo de repetição.
-
As exceções que não acionarão um processo de repetição. Por padrão, nenhuma exceção está excluída.
As seções a seguir descrevem as diversas formas para implementar uma estratégia de repetição exponencial.
Repetição exponencial com @ExponentialRetry
A forma mais simples de implementar uma estratégia de repetição exponencial para uma atividade é aplicar uma anotação @ExponentialRetry
à atividade na definição da interface. Se a atividade falhar, a estrutura gerencia o processo de repetição automaticamente, com base nos valores da opção especificada. O padrão básico é:
-
Aplique
@ExponentialRetry
às atividades apropriadas e especifique a configuração de repetição. -
Se uma atividade anotada falhar, a estrutura repete automaticamente a atividade de acordo com a configuração especificada pelos argumentos da anotação.
O operador de fluxo de trabalho ExponentialRetryAnnotationWorkflow
implementa a estratégia de repetição exponencial usando uma anotação @ExponentialRetry
. Ele usa uma atividade unreliableActivity
, cuja definição de interface é implementada nas ExponentialRetryAnnotationActivities
, da seguinte forma:
@Activities(version = "1.0") @ActivityRegistrationOptions( defaultTaskScheduleToStartTimeoutSeconds = 30, defaultTaskStartToCloseTimeoutSeconds = 30) public interface ExponentialRetryAnnotationActivities { @ExponentialRetry( initialRetryIntervalSeconds = 5, maximumAttempts = 5, exceptionsToRetry = IllegalStateException.class) public void unreliableActivity(); }
As opções @ExponentialRetry
especificam a seguinte estratégia:
-
Repetir somente se a atividade lançar uma
IllegalStateException
. -
Usar um tempo de espera inicial de 5 segundos.
-
Não mais que cinco tentativas de repetição.
A interface do fluxo de trabalho é implementada no RetryWorkflow
e possui um método, process
, que é o ponto de entrada do fluxo de trabalho. O operador do fluxo de trabalho é implementado em ExponentialRetryAnnotationWorkflowImpl
, da seguinte maneira:
public class ExponentialRetryAnnotationWorkflowImpl implements RetryWorkflow { public void process() { handleUnreliableActivity(); } public void handleUnreliableActivity() { client.unreliableActivity(); } }
O fluxo de trabalho funciona da seguinte forma:
-
process
executa o método síncronohandleUnreliableActivity
. -
handleUnreliableActivity
executa a atividadeunreliableActivity
.
Se a atividade falhar ao lançar uma IllegalStateException
, a estrutura executa automaticamente a estratégia de repetição especificada nas ExponentialRetryAnnotationActivities
.
Repetição exponencial com a classe RetryDecorator
O uso do @ExponentialRetry
é simples. No entanto, a configuração é estática e definida durante a compilação, portanto a estrutura usa a mesma estratégia de repetição sempre que a atividade falhar. É possível implementar uma estratégia de repetição exponencial mais flexível usando a classe RetryDecorator
, que permite especificar a configuração durante o tempo de execução e alterá-la conforme necessário. O padrão básico é:
-
Crie e configure um objeto
ExponentialRetryPolicy
que especifica a configuração de repetição. -
Crie um objeto
RetryDecorator
e envie o objetoExponentialRetryPolicy
da Etapa 1 para o construtor. -
Aplique o objeto decorador à atividade enviando o nome de classe do cliente da atividade para o método de decoração do objeto
RetryDecorator
. -
Execute a atividade.
Se a atividade falhar, a estrutura repete a atividade de acordo com a configuração do objeto ExponentialRetryPolicy
. Altere a configuração de repetição conforme necessário modificando esse objeto.
nota
A anotação @ExponentialRetry
e a classe RetryDecorator
são mutuamente exclusivas. Não é possível usar RetryDecorator
para substituir dinamicamente uma política de repetição especificada por uma anotação @ExponentialRetry
.
A implementação de fluxo de trabalho a seguir mostra como usar a classe RetryDecorator
para implementar uma estratégia de repetição exponencial. Ela usa uma atividade unreliableActivity
que não possui uma anotação @ExponentialRetry
. A interface do fluxo de trabalho é implementada no RetryWorkflow
e possui um método, process
, que é o ponto de entrada do fluxo de trabalho. O operador do fluxo de trabalho é implementado em DecoratorRetryWorkflowImpl
, da seguinte maneira:
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(); } }
O fluxo de trabalho funciona da seguinte forma:
-
process
cria e configura um objetoExponentialRetryPolicy
ao:-
Enviar o intervalo inicial de repetição ao construtor.
-
Chamar o método
withMaximumAttempts
do objeto para definir o número máximo de tentativas como cinco.ExponentialRetryPolicy
expõe outros objetoswith
que você pode usar para especificar outras opções de configuração.
-
-
process
cria um objetoRetryDecorator
chamadoretryDecorator
e envia o objetoExponentialRetryPolicy
da Etapa 1 ao construtor. -
process
aplica o decorador à atividade chamando o métodoretryDecorator.decorate
e enviando-lhe o nome de classe do cliente da atividade. -
handleUnreliableActivity
executa a atividade.
Se a atividade falhar, a estrutura repete-a de acordo com a configuração especificada na Etapa 1.
nota
Muitos dos métodos with
da classe ExponentialRetryPolicy
possuem um método set
correspondente que pode ser chamado para modificar a opção de configuração correspondente a qualquer momento: setBackoffCoefficient
, setMaximumAttempts
, setMaximumRetryIntervalSeconds
e setMaximumRetryExpirationIntervalSeconds
.
Repetição exponencial com a classe AsyncRetryingExecutor
A classe RetryDecorator
oferece mais flexibilidade ao configurar o processo de repetição que a @ExponentialRetry
, mas a estrutura ainda executa as tentativas de repetição automaticamente, com base na configuração atual do objeto ExponentialRetryPolicy
. Uma abordagem mais flexível é usar a classe AsyncRetryingExecutor
. Além de permitir que você configure o processo de repetição durante o tempo de execução, a estrutura chama um método AsyncRunnable.run
implementado pelo usuário para executar cada tentativa de repetição em vez de simplesmente executar a atividade.
O padrão básico é:
-
Crie e configure um objeto
ExponentialRetryPolicy
para especificar a configuração de repetição. -
Crie um objeto
AsyncRetryingExecutor
e envie-lhe o objetoExponentialRetryPolicy
e uma instância do clock do fluxo de trabalho. -
Implemente uma classe aninhada
TryCatch
ouTryCatchFinally
anônima. -
Implemente uma classe
AsyncRunnable
anônima e substitua o métodorun
para implementar o código personalizado para execução da atividade. -
Substitua
doTry
para chamar o métodoexecute
do objetoAsyncRetryingExecutor
enviar-lhe a classeAsyncRunnable
da Etapa 4. O objetoAsyncRetryingExecutor
chamaAsyncRunnable.run
para executar a atividade. -
Se a atividade falhar, o objeto
AsyncRetryingExecutor
chama o métodoAsyncRunnable.run
novamente, de acordo com a política de repetição especificada na Etapa 1.
O fluxo de trabalho a seguir mostra como usar a classe AsyncRetryingExecutor
para implementar uma estratégia de repetição exponencial. Ele usa a mesma atividade unreliableActivity
que o fluxo de trabalho DecoratorRetryWorkflow
discutido anteriormente. A interface do fluxo de trabalho é implementada no RetryWorkflow
e possui um método, process
, que é o ponto de entrada do fluxo de trabalho. O operador do fluxo de trabalho é implementado em AsyncExecutorRetryWorkflowImpl
, da seguinte maneira:
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 { } }; } }
O fluxo de trabalho funciona da seguinte forma:
-
process
chama o métodohandleUnreliableActivity
e envia-lhe as definições de configuração. -
handleUnreliableActivity
usa as definições de configuração da Etapa 1 para criar um objetoExponentialRetryPolicy
,retryPolicy
. -
handleUnreliableActivity
cria um objetoAsyncRetryExecutor
,executor
, e envia o objetoExponentialRetryPolicy
da Etapa 2 e uma instância do clock do fluxo de trabalho ao construtor -
handleUnreliableActivity
implementa uma classe aninhadaTryCatch
anônima e substitui os métodosdoTry
edoCatch
para executar as tentativas de repetição e gerenciar quaisquer exceções. -
doTry
cria uma classeAsyncRunnable
anônima e substitui o métodorun
para implementar o código personalizado para executar aunreliableActivity
. Por simplicidade,run
apenas executa a atividade, mas você pode implementar abordagens mais sofisticadas conforme for apropriado. -
doTry
chamaexecutor.execute
e envia-lhe o objetoAsyncRunnable
.execute
chama o métodoAsyncRunnable
do objetorun
para executar a atividade. -
Se a atividade falhar, o executor chama
run
novamente, de acordo com a configuração do objetoretryPolicy
.
Para obter mais discussões sobre como usar a classe TryCatch
para gerenciar erros, consulte AWS Flow Framework para exceções de Java.
Estratégia de repetição personalizada
A abordagem mais flexível para repetir atividades com falha é uma estratégia personalizada, que chama recursivamente um método assíncrono que executa a tentativa de repetição, de forma bem semelhante à estratégia repetir até o sucesso. No entanto, em vez de simplesmente executar a atividade novamente, implemente a lógica personalizada que decide se e como executar cada tentativa sucessiva de repetição. O padrão básico é:
-
Crie um objeto de status
Settable<T>
, que é usado para indicar se a atividade falhou. -
Implemente uma classe aninhada
TryCatch
ouTryCatchFinally
. -
doTry
executa a atividade. -
Se a atividade falhar,
doCatch
define o objeto de status para indicar que a atividade falhou. -
Chame um método assíncrono de gerenciamento de falhas e envie-lhe o objeto de status. O método adia a execução até que
TryCatch
ouTryCatchFinally
conclua. -
O método de gerenciamento de falhas decide se deve repetir a atividade e, se sim, quando.
O fluxo de trabalho a seguir mostra como implementar uma estratégia de repetição personalizada. Ele usa a mesma atividade unreliableActivity
que os fluxos de trabalho DecoratorRetryWorkflow
e AsyncExecutorRetryWorkflow
. A interface do fluxo de trabalho é implementada no RetryWorkflow
e possui um método, process
, que é o ponto de entrada do fluxo de trabalho. O operador do fluxo de trabalho é implementado em CustomLogicRetryWorkflowImpl
, da seguinte maneira:
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; } }
O fluxo de trabalho funciona da seguinte forma:
-
process
chama o método assíncronocallActivityWithRetry
. -
callActivityWithRetry
cria um objetoSettable<Throwable>
chamado failure que é usado para indicar se a atividade falhou.Settable<T>
é derivado dePromise<T>
e funciona da mesma forma, mas você define o valor de um objetoSettable<T>
manualmente. -
callActivityWithRetry
implementa uma classe aninhadaTryCatchFinally
anônima para processar todas as exceções lançadas pelaunreliableActivity
. Para obter mais discussões sobre como gerenciar as exceções lançadas pelo código assíncrono, consulte AWS Flow Framework para exceções de Java. -
doTry
executa aunreliableActivity
. -
Se
unreliableActivity
lança uma exceção, a estrutura chamadoCatch
e envia-lhe o objeto de exceção.doCatch
definefailure
para o objeto de exceção, o que indica que a atividade falhou e coloca o objeto em um estado pronto. -
doFinally
verifica sefailure
está pronto, que será verdadeiro somente sefailure
foi definido pordoCatch
.-
Se
failure
estiver pronto,doFinally
não faz nada. -
Se
failure
não estiver pronto, a atividade foi concluída edoFinally
define failure comonull
.
-
-
callActivityWithRetry
chama o método assíncronoretryOnFailure
e envia-lhe o failure. Como failure é do tipoSettable<T>
,callActivityWithRetry
adia a execução até que failure esteja pronto, o que ocorre apósTryCatchFinally
concluir. -
retryOnFailure
obtém o valor de failure.-
Se failure está definido como null, a tentativa de repetição foi bem-sucedida.
retryOnFailure
não faz nada, o que encerra o processo de repetição. -
Se failure for definido como um objeto de exceção e
shouldRetry
retornar verdadeiro,retryOnFailure
chamacallActivityWithRetry
para repetir a atividade.shouldRetry
implementa a lógica personalizada para decidir se deve repetir uma atividade com falha. Por simplicidade,shouldRetry
sempre retornatrue
eretryOnFailure
executa a atividade imediatamente, mas você pode implementar uma lógica mais sofisticada conforme necessário.
-
-
As etapas 2 a 8 se repetem até que
unreliableActivity
seja concluído oushouldRetry
decida interromper o processo.
nota
doCatch
não lida com o processo de repetição, ele simplesmente define failure para indicar que a atividade falhou. A processo de repetição é gerenciado pelo método assíncrono retryOnFailure
, que adia a execução até que TryCatch
seja concluído. O motivo desta abordagem é que, se você repetir uma atividade no doCatch
, não é possível cancelá-la. Repetir a atividade em retryOnFailure
permite executar atividades canceláveis.