Spring AMQP事务处理和重试

时间:2015-11-10 09:42:13

标签: spring rabbitmq spring-amqp

我正在尝试有关事务性消息处理的Srpring AMQP功能。

我有以下设置 - 我有一个注释为@Transactional

的消息使用者
@Transactional
    public void handleMessage(EventPayload event) {
        Shop shop = new Shop();
        shop.setName(event.getName());

        Shop savedShop = shopService.create(shop);

        log.info("Created shop {} from event {}", shop, event);
    }

shopService.create我保存商店并发送有关创作的另一条消息:

@Transactional(propagation = REQUIRED)
@Component
public class ShopService {

   ...

    public Shop create(Shop shop) {
        eventPublisher.publish(new EventPayload(shop.getName()));
        return shopRepository.save(shop);
    }
}

我想实现以下目的 - 如果数据库操作成功,则create方法中发送的消息应该转到代理。如果失败,则不发送消息并回滚收到的消息。

我也配置了重试 - 所以我希望每封邮件在被拒绝之前重试3次:

@Bean
    public RetryOperationsInterceptor retryOperationsInterceptor() {
        return RetryInterceptorBuilder.stateless()
                .maxAttempts(3)
                .backOffOptions(1000, 2.0, 10000)
                .build();
    }

我正在观察以下行为:

当我按如下方式配置容器时,将重试该消息3次,但每次shopService.create中的消息都发送给代理时:

@Bean
    SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory,
                                                            MessageListenerAdapter listenerAdapter) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.setQueueNames(testEventSubscriberQueue().getName());
        container.setMessageListener(listenerAdapter);
        container.setChannelTransacted(true);
        container.setAdviceChain(new Advice[]{retryOperationsInterceptor()});
        return container;
    }

所以我尝试将PlatformTransactionManager传递给容器 -

@Bean
    SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory,
                                                            MessageListenerAdapter listenerAdapter,
                                                            PlatformTransactionManager transactionManager) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.setQueueNames(testEventSubscriberQueue().getName());
        container.setMessageListener(listenerAdapter);
        container.setChannelTransacted(true);
        container.setTransactionManager(transactionManager);
        container.setAdviceChain(new Advice[]{retryOperationsInterceptor()});
        return container;
    }

现在,shopService.create中发送的消息只会在数据库事务成功时发送给代理 - 这就是我想要的 - 但是消息现在无限期地重试 - 并且在配置完3次退出后不会被丢弃。但似乎应用了backOff设置 - 因此重试之间有一段时间。

从业务角度来看,所描述的设置并不合理 - 我正在尝试理解和评估交易功能。

我使用的是spring-amqp 1.5.1.RELEASE

感谢任何提示。

1 个答案:

答案 0 :(得分:0)

我有相同的要求,一个用@RabbitListener注释的@Transactional,我想重试一次。使用以下配置,它甚至可以实现无状态运行:

   @Bean
   public RetryOperationsInterceptor retryOperationsInterceptor( ) {
      return RetryInterceptorBuilder.stateless()
            .maxAttempts( 3 )
            .recoverer( new RejectAndDontRequeueRecoverer() )
            .backOffOptions(1000, 2, 10000)
            .build();
   }

   @Bean
   public Jackson2JsonMessageConverter producerJackson2MessageConverter( ObjectMapper objectMapper ) {
      Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter( objectMapper );
      jackson2JsonMessageConverter.setCreateMessageIds( true );
      return jackson2JsonMessageConverter;
   }      

   @Bean
   SimpleRabbitListenerContainerFactory  rabbitListenerContainerFactory( ConnectionFactory connectionFactory,
                                                            PlatformTransactionManager transactionManager,
                                                                   Jackson2JsonMessageConverter converter) {
      SimpleRabbitListenerContainerFactory  container = new SimpleRabbitListenerContainerFactory ();
      container.setConnectionFactory(connectionFactory);

      container.setChannelTransacted(true);
      container.setTransactionManager(transactionManager);

      container.setAdviceChain( retryOperationsInterceptor() );

      container.setMessageConverter( converter );
      return container;
   }

要使用stateless(),请使用RejectAndDontRequeueRecoverer,这一点很重要,因为否则可以进行重试,但使用方默认情况下会将消息重新放入队列。然后,消费者将再次检索它,应用重试策略,然后将其无限地放回到队列中。