我正在使用Spring AMQP发送消息,并能够对“自定义”异常执行重试。假设我有一个Receiver抛出一个自定义异常“EventException”,为此,我希望有一个 n 重试次数(在我们的例子中为5)。在重试之间我也希望延迟5秒。这是我的源代码:
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
final static String queueName = "testing-queue";
@Autowired
AnnotationConfigApplicationContext context;
@Autowired
RabbitTemplate rabbitTemplate;
@Bean
Queue queue() {
Map<String, Object> arguments = new HashMap<String, Object>();
arguments.put("x-dead-letter-exchange", "dead-letter-exchange");
Queue queue = new Queue(queueName, true, false, false, arguments);
return queue;
}
@Bean
TopicExchange exchange() {
return new TopicExchange("testing-exchange");
}
@Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(queueName);
}
@Bean
Queue deadLetterQueue() {
return new Queue("dead-letter-queue", true);
}
@Bean
FanoutExchange deadLetterExchange() {
return new FanoutExchange("dead-letter-exchange");
}
@Bean
Binding deadLetterBinding(Queue deadLetterQueue, FanoutExchange deadLetterExchange) {
return BindingBuilder.bind(deadLetterQueue).to(deadLetterExchange);
}
@Bean
ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory =
new CachingConnectionFactory("localhost");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory;
}
@Bean
SimpleMessageListenerContainer container(
ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter,
RetryOperationsInterceptor interceptor) {
Advice[] adviceChain = { interceptor };
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(queueName);
container.setAdviceChain(adviceChain);
container.setMessageListener(listenerAdapter);
return container;
}
@Bean
Receiver receiver() {
return new Receiver();
}
@Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
MessageListenerAdapter adapter =
new MessageListenerAdapter(receiver, "receiveMessage");
return adapter;
}
@Bean
RetryOperations retryTemplate() {
Map<Class<? extends Throwable>, Boolean> retryableExceptions =
new HashMap<Class<? extends Throwable>, Boolean>();
retryableExceptions.put(EventException.class, false);
FixedBackOffPolicy backoffPolicy = new FixedBackOffPolicy();
backoffPolicy.setBackOffPeriod(5000);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(backoffPolicy);
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(5, retryableExceptions));
return retryTemplate;
}
@Bean
RetryOperationsInterceptor interceptor(RetryOperations retryTemplate) {
RetryOperationsInterceptor interceptor = new RetryOperationsInterceptor();
interceptor.setRecoverer(new CustomMessageRecover());
interceptor.setRetryOperations(retryTemplate);
return interceptor;
// return RetryInterceptorBuilder
// .stateless()
// //.retryOperations(retryTemplate)
// .maxAttempts(5)
// .recoverer(new CustomMessageRecover()).build();
}
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println("Sending message...");
rabbitTemplate.convertAndSend(queueName, "Hello from RabbitMQ!");
context.close();
}
public class Receiver {
public void receiveMessage(String message) throws Exception {
System.out.println("!!!!!!!!Message has been recieved!!!!!!");
throw new EventException("TESTING");
}
}
public class CustomMessageRecover implements MethodInvocationRecoverer<Void> {
@Override
public Void recover(Object[] args, Throwable cause) {
System.out.println("IN THE RECOVER ZONE!!!");
throw new AmqpRejectAndDontRequeueException(cause);
}
}
class EventException extends Exception {
private static final long serialVersionUID = 1L;
public EventException() {}
public EventException(String message) {
super(message);
}
}
}
现在在代码中,正如您所看到的,我正在使用 RetryOperationsInterceptor 来拦截并检查它被抛出的异常类型并以此为基础,做出决定重试与否,以及重试之间的延迟。
为此,我设置了RetryTemplate Bean的 backoffPolicy 和 retryPolicy ,并将其注入RetryOperationsInterceptor。
如果有人能帮助我并告诉我为什么重试和重试之间的延迟不起作用,我将不胜感激。我的消息直接发送到死信交换,没有重试和延迟发生。
谢谢!
答案 0 :(得分:1)
您的问题在这里:
retryableExceptions.put(EventException.class, false);
请找到SimpleRetryPolicy
代码:
public boolean canRetry(RetryContext context) {
Throwable t = context.getLastThrowable();
return (t == null || retryForException(t)) && context.getRetryCount() < maxAttempts;
}
并进一步:
private boolean retryForException(Throwable ex) {
return retryableClassifier.classify(ex);
}
由于您为false
指定了EventException
,因此它不会是retryable
。因此任何重试和退避。