在第一次重试尝试中执行操作以将消息保存在数据库

时间:2018-05-11 16:44:05

标签: spring rabbitmq spring-amqp

我试图在数据库中保存每个发送到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队列。

我的意识是在重试拦截器中找到可以在第一次尝试时调用服务的东西,只保存一次消息。

我也开放了另一个想法来解决这个问题,比如保存带有消息的尝试号码,只是为了表明这不是保存在数据库上的重复消息,而是在不同的尝试中使用相同的消息

2 个答案:

答案 0 :(得分:1)

注意你如何申请重审:

Advice[] adviceChain = {defaultRetryOperationsInterceptor};
factory.setAdviceChain(adviceChain);

您可以编写自己的MethodInterceptor来写入数据库。 当你在 {/ 1>}之前按顺序定义这个自定义建议时,数据库保存只会被调用一次,因为这样的操作将在重试之外甚至之前发生。 / p>

在文档中查看有关AOP的更多信息:https://docs.spring.io/spring/docs/5.0.6.RELEASE/spring-framework-reference/core.html#aop

答案 1 :(得分:0)

首先,我要感谢ArtemGarry帮助我解决问题。

至少有四种解决方案:

  1. Advice解决方案(请参阅Garry的answer
  2. 来自RetrySynchronizationManager
  3. 的计数器
  4. messageId解决方案
  5. 使用statefull RetryOperationsInterceptor
  6. 来自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