我正在尝试有关事务性消息处理的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
感谢任何提示。
答案 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
,这一点很重要,因为否则可以进行重试,但使用方默认情况下会将消息重新放入队列。然后,消费者将再次检索它,应用重试策略,然后将其无限地放回到队列中。