RabbitMQ幂等性、限流、消息确认代码实现

一、如何保证消息不重复消费

1.简介

消息幂等性,其实就是保证一个消息不管因为什么原因都只会被消费一次,正常情况下,消费者消费完消息之后,会发送一个ack给生产者,但是中途有可能因为网络等原因导致生产者未收到确认信息,导致一条消息被重复消费。

如何避免重复消费的问题

1.使用一个唯一的id。消费之前根据id去判断消息是否被消费过,未被消费则正常使用消息。

2.使用redis只能有一个key的特性。

我们这里只介绍第一种方法,SpringAMQP中给出了这种问题的解决方案。

关于配置问题和入门使用SpringAMQP,大家可以看我之前写的文章。

SpringAMQP快速入门-CSDN博客

2.代码介绍

在这里我们直接展示生产者和消费者代码,看一下具体如何通过代码实现。

生产者代码:

@SpringBootTest
class DemoMqSendApplicationTests {
​
    @Autowired
    private RabbitTemplate rabbitTemplate;
​
    @Test
    void contextLoads() {
        //声明交换机
        String exchange="simple.fanout";
        //amqp自带的配置,可以设置唯一id
        MessageProperties properties = new MessageProperties();
        //在这里我们使用uuid作为唯一id来进行幂等性判断
        properties.setMessageId(UUID.randomUUID().toString());
        properties.setContentType("text/plain");
        properties.setContentEncoding("utf-8");
        //将配置和要发送的消息保存一下
        Message message = new Message("hello".getBytes(), properties);
        //进行消息的发送
        rabbitTemplate.convertAndSend(exchange,"", message);
​
​
    }
​
}

监听者代码:

@Component
@Slf4j
public class Listener {
​
    @RabbitListener(queues = "simple.queue")
    public void listener(Message message, Channel channel) {
        //获取到发送的信息
        byte[] body = message.getBody();
        String msg = new String(body);
        //获取到对应的配置
        MessageProperties properties = message.getMessageProperties();
        //获取到唯一的业务id
        String messageId = properties.getMessageId();
        /**
         * 在这里进行业务判断,如果数据库中有这个id则不进行操作,否则进行操作,保证幂等性
         */
​
        log.info("listener: {}", msg);
        log.info("listener: {}", message.getMessageProperties().getMessageId());
        
    }
​
}
listener: hello listenerID: 4b40e96e-2c39-4880-bea9-25d673a57a9a

根据两条日志看到唯一的id就是4b40e96e-2c39-4880-bea9-25d673a57a9a,发送的消息就是hello

拿到id之后,在消费消息前,先去数据库查询这条消息是否存在消费记录,没有就执行insert操作,如果有就代表已经被消费了,则不进行处理。

二、如何保证消息不丢失
1.介绍

消息从生产端到消费端消费要经过3个步骤:

  1. 生产端发送消息到RabbitMQ;

  2. RabbitMQ发送消息到消费端;

  3. 消费端消费这条消息;

在这里不对12进行详细的讲解,发生最多的也就是消费端接收消息丢失。如果想知道12具体操作,以后可以具体介绍。

2.实现
@RabbitListener(queues = "simple.queue2")
public void listener2(Message message, Channel channel) {
    try {
        byte[] body = message.getBody();
        //拿到接收到的消息
        String msg = new String(body, StandardCharsets.UTF_8);
        //获取配置文件
        MessageProperties properties = message.getMessageProperties();
        //获取到唯一标识符
        long tag = properties.getDeliveryTag();
        //消息确认机制
        channel.basicAck(tag, false);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

在这里的手动确认机制我们使用了channel.basicAck来实现,里面有两个参数,分别是一个long类型的唯一标识符和布尔类型的参数。第一个参数对应的就是配置里面的DeliveryTag,每条消息的标识符都是唯一的。布尔类型参数代表的是当为true时就是确认多条消息,false的话就是只确认这一条消息,一般我们都是false。

还需要再配置文件中加上simple:acknowledge-mode: manual就是开启手动确认。

listener:
  simple:
    acknowledge-mode: manual
三、如何进行限流
1.介绍

最后一个问题就是如何进行限流,为什么要限流?防止数据量突然特别大导致服务崩溃。

2.实现

使用channel.basicQos(int prefetchSize, int prefetchCount, boolean global)来实现

参数介绍:

prefetchSize:表示消息的大小,0的话不限制大小

prefetchCount:表示消息的数量

global:true表示该通道下的所有消费者都适用这个策略,而false表示只有当前这一个消费者适用这个策略。

代码实现:

@RabbitListener(queues = "simple.queue2")
public void listener2(Message message, Channel channel) {
    try {
        //开启限流
        channel.basicQos(0,1,false);
        byte[] body = message.getBody();
        //拿到接收到的消息
        String msg = new String(body, StandardCharsets.UTF_8);
        //获取配置文件
        MessageProperties properties = message.getMessageProperties();
        //获取到唯一标识符
        long tag = properties.getDeliveryTag();
        //消息确认机制
        channel.basicAck(tag, false);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

这里的限流表示不限制消息大小,但是限制消息数量,,一次只能发送一条消息。

为什么要限制流量和大小呢?

限流肯定都可以理解,突然的大流量会导致服务崩溃,如果流量不是很多,但是数据量特别大,比如一个人一天吃一个馒头,今天还是吃一个,但是有10公斤重,也会导致服务崩溃。

注意:限流和手动确认机制最好一起使用。

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值