Guia de repetição do Spring

1. Visão Geral

O Spring Retry fornece funcionalidade para recuperar automaticamente as operações com falha. Isso é útil em situações em que o erro pode ser transitório, como uma falha de rede temporária.

Neste tutorial, veremos  várias maneiras de tentar novamente com Spring : Annotations, RetryTemplate  e Callbacks.

2. Dependências do Maven

Vamos começar adicionando  a dependência spring-retry  ao nosso  arquivo pom.xml  :

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>2.0.0</version>
</dependency>

Também precisamos adicionar o Spring AOP ao nosso projeto:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

Confira o Maven Central para obter as versões mais recentes das  dependências spring-retry e spring-aspects  .

3. Ative a repetição de mola

Para ativar as tentativas do Spring em nosso aplicativo, precisamos adicionar a anotação @EnableRetry à nossa  classe @Configuration  :

@Configuration
@EnableRetry
public class AppConfig { ... }

4. Tente novamente usando o Spring

4.1. Sem recuperação @Retryable

Podemos   adicionar funcionalidade de repetição a um método usando a anotação @Retryable :

@Service
public interface MyService { 

    @Retryable 
    void retryService(String sql); 
}

Como não especificamos nenhuma exceção aqui, todas as exceções serão tentadas para serem repetidas. Além disso, quando o número máximo de tentativas for atingido e a exceção ainda estiver presente, será lançada uma ExhaustedRetryException.

De acordo com   o comportamento padrão de @Retryable , as novas tentativas podem ocorrer até três vezes, com um atraso de um segundo entre as tentativas.

4.2.  @Retryable e @Recover

Agora vamos   adicionar um método de recuperação usando a anotação @Recover :

@Service
public interface MyService { 

    @Retryable(retryFor = SQLException.class)
    void retryServiceWithRecovery(String sql) throws SQLException; 

    @Recover
    void recover(SQLException e, String sql); 
}

Aqui,  tente novamente quando SQLException  for lançada .  A anotação @Recover define um único método de recuperação quando um método @Retryable falha com a exceção especificada.

Portanto, se  o método retryServiceWithRecovery  continuar lançando um SqlException após três tentativas  o método retrieve()  será chamado.

Um manipulador de recuperação deve ter  um primeiro parâmetro do tipo Throwable  (opcional) e um tipo de retorno do mesmo.  Os parâmetros a seguir são preenchidos na mesma ordem da lista de parâmetros do método com falha.

4.3. Personalize o comportamento de @Retryable

Para personalizar o comportamento de repetição, podemos usar os parâmetros  maxTry  e backoff :

@Service
public interface MyService {

    @Retryable(retryFor = SQLException.class, maxAttempts = 2, backoff = @Backoff(delay = 100))
    void retryServiceWithCustomization(String sql) throws SQLException;
}

Haverá um máximo de duas tentativas e um atraso de 100ms.

4.4. Usando as propriedades da mola

Também podemos usar propriedades na anotação @Retryable .

Para demonstrar isso, veremos como externalizar os valores de delay e max_attempts em um arquivo de propriedades.

Primeiro, vamos   definir as propriedades em um arquivo chamado retryConfig . propriedades :

retry.maxAttempts=2
retry.maxDelay=100

Em seguida, instruímos nossa classe @Configuration a carregar este arquivo:

// ...
@PropertySource("classpath:retryConfig.properties")
public class AppConfig { ... }

Por fim, podemos injetar   os valores de retry.maxTry  e  retry.maxDelay na definição @Retryable :

@Service 
public interface MyService {

    @Retryable(retryFor = SQLException.class, maxAttemptsExpression = "${retry.maxAttempts}",
               backoff = @Backoff(delayExpression = "${retry.maxDelay}")) 
    void retryServiceWithExternalConfiguration(String sql) throws SQLException; 
}

Observe que agora usamos  maxTrysExpression  e  delayExpression em vez de  maxTry  e  delay .

5.  Repita o modelo

5.1.  Repetir a operação

O Spring Retry fornece  a interface RetryOperations  , que fornece um conjunto de   métodos execute() :

public interface RetryOperations {

    <T> T execute(RetryCallback<T> retryCallback) throws Exception;

    ...
}

RetryCallback  é  um parâmetro de execute()  e é uma interface que permite a lógica de negócios que precisa ser repetida quando uma inserção falha:

public interface RetryCallback<T> {

    T doWithRetry(RetryContext context) throws Throwable;
}

5.2 . Repetir a configuração do modelo

RetryTemplate  é  a implementação de RetryOperations .

Vamos configurar um  bean RetryTemplate  na classe @Configuration :

@Configuration
public class AppConfig {
    
    //...
    
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
		
        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(2000l);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(2);
        retryTemplate.setRetryPolicy(retryPolicy);
		
        return retryTemplate;
    }
}

Uma política de repetição determina quando uma operação deve ser repetida .

A estratégia de repetição simples é usada para repetir um número fixo de vezes. Por outro lado, BackOffPolicy  é usado para controlar a espera entre tentativas de repetição.

Finalmente, FixedBackOffPolicy  faz uma pausa por um período fixo de tempo antes de continuar.

5.3. Usando modelos de repetição

Para executar o código com tratamento de repetição, podemos chamar  o método retryTemplate.execute()  :

retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
    @Override
    public Void doWithRetry(RetryContext arg0) {
        myService.templateRetryService();
        ...
    }
});

Podemos usar expressões lambda em vez de classes anônimas:

retryTemplate.execute(arg0 -> {
    myService.templateRetryService();
    return null;
});

6. Público

O ouvinte fornece retornos de chamada adicionais ao tentar novamente. Podemos usá-los para várias preocupações transversais em diferentes tentativas.

6.1. Adicionar retornos de chamada

Os retornos de chamada são fornecidos na   interface RetryListener :

public class DefaultListenerSupport extends RetryListenerSupport {
    
    @Override
    public <T, E extends Throwable> void close(RetryContext context,
      RetryCallback<T, E> callback, Throwable throwable) {
        logger.info("onClose");
        ...
        super.close(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context,
      RetryCallback<T, E> callback, Throwable throwable) {
        logger.info("onError"); 
        ...
        super.onError(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context,
      RetryCallback<T, E> callback) {
        logger.info("onOpen");
        ...
        return super.open(context, callback);
    }
}

Os retornos de chamada abertos e fechados ocorrem antes e depois de toda a repetição, enquanto  onError  se aplica a uma única  chamada RetryCallback  .

6.2. Registrando ouvintes

Em seguida, registramos o ouvinte ( DefaultListenerSupport) em nosso  bean RetryTemplate :

@Configuration
public class AppConfig {
    ...

    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        ...
        retryTemplate.registerListener(new DefaultListenerSupport());
        return retryTemplate;
    }
}

7. Resultados do teste

Para completar nosso exemplo, vamos verificar os resultados:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  classes = AppConfig.class,
  loader = AnnotationConfigContextLoader.class)
public class SpringRetryIntegrationTest {

    @Autowired
    private MyService myService;

    @Autowired
    private RetryTemplate retryTemplate;

    @Test(expected = RuntimeException.class)
    public void givenTemplateRetryService_whenCallWithException_thenRetry() {
        retryTemplate.execute(arg0 -> {
            myService.templateRetryService();
            return null;
        });
    }
}

Como pode ser visto no log de teste, configuramos corretamente  RetryTemplate  e  RetryListener :

2020-01-09 20:04:10 [main] INFO  o.b.s.DefaultListenerSupport - onOpen 
2020-01-09 20:04:10 [main] INFO  o.baeldung.springretry.MyServiceImpl
- throw RuntimeException in method templateRetryService() 
2020-01-09 20:04:10 [main] INFO  o.b.s.DefaultListenerSupport - onError 
2020-01-09 20:04:12 [main] INFO  o.baeldung.springretry.MyServiceImpl
- throw RuntimeException in method templateRetryService() 
2020-01-09 20:04:12 [main] INFO  o.b.s.DefaultListenerSupport - onError 
2020-01-09 20:04:12 [main] INFO  o.b.s.DefaultListenerSupport - onClose

8.  Conclusão

Neste artigo, vimos como usar  o Spring Retry usando anotações, RetryTemplate e callback listeners.

O código-fonte desses exemplos está  disponível no GitHub .

Guess you like

Origin blog.csdn.net/allway2/article/details/130438414