mq学习笔记

如何防止消息丢失

一条消息从生产到消费,可以划分为三个阶段:

  1. 生产阶段。Producer发送消息,经过网络传输到达Broker;
  2. Broker保存和处理消息;
  3. Consumer从Broker拉取消息,进行消费。

  4. 阶段1有可能因为网络原因或者Broker自身挂掉了,导致消息丢失。一般利用mq自带的comfirm机制来解决,当Broker收到消息并成功处理后回复ACK给生产者。 生产者在发布消息的时候要确保处理消息发送失败时候的异常 catch,然后进行重试或者保存日志,以确保消息可以在后续发送成功。

  5. 阶段2中Broker收到消息但是如果rabbitmq服务重启了或者机器重启了,消息还没有持久化到硬盘,消息也会丢失,因此可以结合持久化来确保消息在这个阶段不丢失。比如在 RabbitMQ中可以把交换机、队列以及消息都进行持久化。 如果开启了消息持久化,则Broker把消息持久化到硬盘里才会回复 ACK 给 Producer。而消息持久化分为同步和异步,同步就是每收到一条消息就同步落盘,性能较差,异步则是先写入缓存,等时机成熟以后再落盘,性能较优。我们可以通过多节点MQ(副本)结合异步持久化的方式,减少消息丢失的概率,比如当消息被发送到两个节点以上的队列上才回复 ACK。

  6. Consumer 可以通过 ACK 机制,在处理完所有业务逻辑以后才回复 ACK,确保消息被正常消费以后才告知 Broker,这样就确保了消息在消费阶段不会丢失数据,需要注意的是消费者要注意重复消息的处理。

处理重复消息

上面说了生产者可能会因为重试机制导致发送重复的消息,所以消费端需要做好消费的幂等性:

  1. 通过业务ID结合数据库的唯一约束
  2. 版本号实现乐观锁
  3. GUID + 分布式锁。给每个消息指定一个GUID,消费时判断这个GUID是否有被消费过,如果没有则进行消费并记录GUID为已消费,并且通过分布式锁防止并发请求实现以上操作的原子性。