我试图在数据库中保存每个发送到RabbitMQ队列的消息,仅用于记录。之后,需要正常处理消息。
问题是:此队列具有配置为RetryOperationsInterceptor
的重试策略,并且每次处理消息时都出现错误,该消息将被重新排队并再次处理。保存消息的逻辑是在侦听器中读取队列,因此我只有3
(我配置的重试次数),而不是只有一个消息保存在数据库中。
查看我的RetryOperationsInterceptor
:
@Bean
public RetryOperationsInterceptor defaultRetryOperationsInterceptor() {
return RetryInterceptorBuilder.stateless()
.maxAttempts(3)
.backOffOptions(2000, 2.0, 10000)
.build();
}
容器工厂:
@Bean(name = FACTORY_CONTAINER_NAME)
public SimpleRabbitListenerContainerFactory factoryQueueExample(ConnectionFactory connectionFactory,
RetryOperationsInterceptor defaultRetryOperationsInterceptor) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(getMessageConverter());
factory.setDefaultRequeueRejected(false);
Advice[] adviceChain = {defaultRetryOperationsInterceptor};
factory.setAdviceChain(adviceChain);
return factory;
}
队列侦听器:
@Slf4j
@Component
@AllArgsConstructor
public class MessageListener {
private final MessageRepository messageRepository;
private final MessageService messageService;
@RabbitListener(queues = MessageConfiguration.QUEUE,
containerFactory = MessageConfiguration.FACTORY_CONTAINER_NAME)
public void process(SomeMessage someMessage) {
messageRepository.save(someMessage); // transform to a entity and save on database
messageService.process(someMessage); // process message
}
}
我不知道是否是相关信息,但此队列也与DLQ相关联。重试后,消息将进入DLQ队列。
我的意识是在重试拦截器中找到可以在第一次尝试时调用服务的东西,只保存一次消息。
我也开放了另一个想法来解决这个问题,比如保存带有消息的尝试号码,只是为了表明这不是保存在数据库上的重复消息,而是在不同的尝试中使用相同的消息
答案 0 :(得分:1)
注意你如何申请重审:
Advice[] adviceChain = {defaultRetryOperationsInterceptor};
factory.setAdviceChain(adviceChain);
您可以编写自己的 在文档中查看有关AOP的更多信息:https://docs.spring.io/spring/docs/5.0.6.RELEASE/spring-framework-reference/core.html#aop MethodInterceptor
来写入数据库。
当你在 {/ 1>}之前按顺序定义这个自定义建议时,数据库保存只会被调用一次,因为这样的操作将在重试之外甚至之前发生。 / p>
答案 1 :(得分:0)
至少有四种解决方案:
Advice
解决方案(请参阅Garry的answer)RetrySynchronizationManager
messageId
解决方案RetryOperationsInterceptor
来自RetrySynchronizationManager
选项的计数器可以很容易地用于我的情况:
int attempts = RetrySynchronizationManager.getContext().getRetryCount();
boolean canSaveOnDatabase = attempts == 0;
发件人必须messageId
选项发送唯一标识该ID的ID。因此,我可以在数据库中保存并使用该信息,以了解该消息是否已经保留。从send
调用某个RabbitTemplate
方法时,可以配置by the MessagePostProcessor。
最后一个选项,状态为RetryOperationsInterceptor
我可以在@Header(AmqpHeaders.REDELIVERED) boolean redelivered
中查找标题RabbitListener
。如果您使用无状态RetryOperationsInterceptor
(我的情况),此选项不起作用,此选项也需要messageId
。