Introdução
Endereço de origem do Spring Retry
A estrutura de repetição Spring Retry suporta o uso declarativo e imperativo.
Requerimentos ambientais
Spring Retry requer Java 1.7 e Maven 3.0.5 (ou superior).
Recomenda-se que Java 1.8 e superior sejam usados para simplificar o código usando expressões lambda.
Declarativo
O uso declarativo é baseado na programação de aspecto AOP, que requer a introdução de aspectjweaver ou spring-boot-starter-aop
pom.xml
<!-- Spring Retry -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Configuration
@EnableRetry
public class Application {
}
@Service
class Service {
@Retryable(RemoteAccessException.class)
public void service() {
// ... do something
}
@Recover
public void recover(RemoteAccessException e) {
// ... panic
}
}
caso real:
@Override
@Retryable(value = {
Exception.class }, maxAttempts = 3, backoff = @Backoff(delay = 5000L, multiplier = 1))
public Object restPostTest(Map map, String url) throws Exception {
logger.info("调用第三方接口");
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//设置访问的Entity
HttpEntity entity = new HttpEntity<>(map, headers);
ResponseEntity<String> result;
JSONObject data = new JSONObject();
//发起一个POST请求
logger.info("发起一个POST请求");
result = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
data = JSONObject.parseObject(result.getBody());
logger.info("返回结果{}",data);
return data;
}
@Recover
public Object recover(Exception e, Map map, String url) {
// ... 放入消息队列或数据库
}
@Retryable Annotation O
método anotado será tentado novamente
quando ocorrer uma exceção. Valor: Especifique a ocorrência da exceção para tentar novamente.
Incluir: O mesmo que o valor, vazio por padrão. Quando excluir também está vazio, todas as exceções são repetidas.
Exclude: Especifique o exceção para não tentar novamente., O padrão é vazio, quando a inclusão também está vazia, todas as exceções são repetidas
maxAttemps: número de tentativas, padrão 3
recuo: mecanismo de compensação de nova tentativa, padrão não
@Backoff annotation
delay: Tentar novamente após o
multiplicador de atraso especificado : especifique o múltiplo do atraso, como delay = 5000l, multiplicador = 2, a primeira tentativa é 5 segundos, a segunda vez é 10 segundos e a terceira vez é 20 segundos
@Recover
Quando as novas tentativas atingirem o número especificado de vezes, o método anotado será chamado de volta e o processamento do log pode ser executado neste método. Deve-se observar que o callback só ocorrerá quando ocorrer a exceção e o tipo de parâmetro de entrada for o mesmo.
Descrição:
- O objeto de classe onde as anotações acima estão localizadas deve ser assumido pelo container spring, usando @Service, @Bean, etc.
- Os valores de retorno dos métodos marcados por @Retryable e @Recover devem ser consistentes!
- Parâmetro do método @Recover, Exception e deve ser colocado na primeira posição do parâmetro e o tipo deve ser consistente com o tipo lançado por @Retryable.
- Os métodos @Retryable não podem mais usar try ... catch para capturar exceções para evitar que as exceções não sejam recebidas.
Programático (imperativo)
RetryTemplate template = new RetryTemplate();
TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
policy.setTimeout(30000L);
template.setRetryPolicy(policy);
Foo result = template.execute(new RetryCallback<Foo>() {
public Foo doWithRetry(RetryContext context) {
// Do stuff that might fail, e.g. webservice operation
return result;
}
});
Uso de cadeia combinado com expressão lambda para simplificar o código
RetryTemplate template = RetryTemplate.builder()
.maxAttempts(3)
.fixedBackoff(1000)
.retryOn(RemoteAccessException.class)
.build();
template.execute(ctx -> {
// ... do something
});
A parte lambda não reescreve a lógica de RetryCallback # doWithRetry.
public interface RetryCallback<T> {
T doWithRetry(RetryContext context) throws Throwable;
}
Ouvinte:
public interface RetryListener {
void open(RetryContext context, RetryCallback<T> callback);
void onError(RetryContext context, RetryCallback<T> callback, Throwable e);
void close(RetryContext context, RetryCallback<T> callback, Throwable e);
}
template.registerListener(new MethodInvocationRetryListenerSupport() {
@Override
protected <T, E extends Throwable> void doClose(RetryContext context,
MethodInvocationRetryCallback<T, E> callback, Throwable throwable) {
monitoringTags.put(labelTagName, callback.getLabel());
Method method = callback.getInvocation()
.getMethod();
monitoringTags.put(classTagName,
method.getDeclaringClass().getSimpleName());
monitoringTags.put(methodTagName, method.getName());
// register a monitoring counter with appropriate tags
// ...
}
});