RabbitMQ消费幂等性

2,354 阅读3分钟

这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战

什么是幂等性

幂等性,简单来说就是对于同一个系统,在同样条件下,一次请求和重复多次请求对资源的影响是一致的,就称该操作为幂等的。比如说如果有一个接口是幂等的,当传入相同条件时,其效果必须是相同的。在RabbitMQ中消费幂等就是指给消费者发送多条同样的消息,消费者只会消费其中的一条。例如,在一次购物中提交订单进行支付时,当网络延迟等其他问题造成消费者重新支付,如果没有幂等性的支持,那么会对同一订单进行两次扣款,这是非常严重的,因此有了幂等性,当对同一个订单进行多次支付时,可以确保只对同一个订单扣款一次。

RabbitMQ消费幂等性

在正常情况下,消费者在消费消息的时候,当消费完毕后,会发送一个确认ack给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。而在前面保证生产端消息可靠性投递方案1中,当生产者发送消息给RabbitMQ后,在Broker返回确认ack之前,RabbitMQ出现了宕机(数据库保存的消息状态仍然为“投递中”),则该消息会被定时任务抓取并重新发送;或者当在网络延迟传输中,消费者出现异常或者消费者延迟消费,会造成进行RabbitMQ重试补偿,那么此时RabbitMQ中就可能会有两条消息,会造成消费者重复消费,此时消费端就需要做幂等性校验,让消费者只消费其中一条消息。实现消费端幂等性、保证同一消息不被重复消费下面介绍一种简单的方案。

  • 为了保证消息不被重复消费,首先要保证每个消息是唯一的,所以可以给每一个消息携带一个唯一的id,流程如下:

    1、消费者监听到消息后获取消息的MsgId(这个MsgId是我们自定义消息的字段,是主键),先去Redis中查询这个MsgId是否存在。也可以生产者发送消息时指给消息对象设置唯一的 MessageID,只有该 MessageID 没有被消费者存入到Redis中即该消息未被消费,这样重发的消息才能在重试机制中再次被消费。

    2、如果不存在,则正常消费消息,并把消息的id存入Redis中。

    3、如果存在则丢弃或者拒绝此消息并不返回队列。

  • 代码:

    1.消费者

    @RabbitListener(queues = "queue1")
    public void getMessageFromQueue1(Channel channel, Message message) throws IOException {
        SetOperations<String, Object> ops = redisTemplate.opsForSet();
        //获取唯一Id
        String msgID = message.getMessageProperties().getHeader(
                "spring_returned_message_correlation");
    
        try{
            if(ops.pop(msgID)!=null) {
            //该订单已经完成扣款,无需再进行扣款
            channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
            return ;
    
            }
            //执行扣款操作
            ..
            ..
    
            //将对应的订单id保存到redis中         
            ops.add(msgID,"ok");
    
            //返回确认ack
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }catch(Exception e){
          
            channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
    
        }
    
    }
    

🏁以上就是对RabbitMQ消费幂等性的简单介绍,如果有错误的地方,还请留言指正,如果觉得本文对你有帮助那就点个赞👍吧😋😻😍

默认标题_动态分割线_2021-07-15-0.gif