乐趣区

关于java:聊聊MQ如何避免消息丢失如何避免重复消费

前言

我在工作中,应用到消息中间件 MQ 的业务还是挺多的,我从事在一家交通行业的公司,业务中常常会波及解决一些守法数据的场景,我的项目中常常会应用到 RabbitMQ,明天想跟大家聊聊怎么防止音讯失落和反复生产是问题。

在咱们产生音讯的时候,如果 MQ 服务器忽然宕机了会呈现什么状况?是不是咱们发过来的音讯全副没有了吗?

是的,个别 MQ 中间件为了进步零碎的吞吐量会把音讯保留在内存中,如果不作其余解决,MQ 服务器一旦宕机,音讯将全副失落。这个是业务不容许的,造成很大的影响。

在遇到这种问题的时候,个别有以下几种解决形式。

如何防止音讯失落

长久化

RabbitMQ 中发消息的时候会有个 durable 参数能够设置,设置为 true,就会长久化。

这样的话 MQ 服务器即便宕机,重启后磁盘文件中有音讯的存储,这样就不会失落了吧。是的这样就肯定概率的保障了音讯不失落。

但还会有个场景,就是音讯刚刚保留到 MQ 内存中,但还没有来得及更新到磁盘文件中,忽然宕机了。这个场景在继续的大量音讯投递的过程中,会很常见。

那怎么办?咱们如何作能力保障肯定会长久化到磁盘下面呢?

confirm 机制

RabbitMQ 利用 confirm 机制来告诉咱们是否长久化胜利?

confirm 机制的原理:

(1)音讯生产者把音讯发送给 MQ,如果接管胜利,MQ 会返回一个 ack 音讯给生产者;

(2)如果音讯接管不胜利,MQ 会返回一个 nack 音讯给生产者;

这样是不是就能够保障 100% 音讯不失落了呢?

如果咱们生产者每发一条音讯,都要 MQ 长久化到磁盘中,而后再发动 ack 或 nack 的回调。这样的话是不是咱们 MQ 的吞吐量很不高,因为每次都要把音讯长久化到磁盘中。写入磁盘这个动作是很慢的。这个在高并发场景下是不可能承受的,吞吐量太低了。

所以 MQ 长久化磁盘实在的实现,是通过异步调用解决的,他是有肯定的机制,如:等到有几千条音讯的时候,会一次性的刷盘到磁盘下面。而不是每来一条音讯,就刷盘一次。

所以 comfirm 机制其实是一个异步监听的机制,是为了保证系统的高吞吐量,这样就导致了还是不可能 100% 保障音讯不失落,因为即便加上了 confirm 机制,音讯在 MQ 内存中还没有刷盘到磁盘就宕机了,还是没法解决。

数据库事务机制

生产者在投递音讯之前,能够在本地数据库建一张音讯表,先把音讯长久化到 Redis 或 DB 中,这样就能够利用本地数据库的事务机制。事务提交胜利后,将音讯表中的音讯转移到音讯队列中。

confirm 机制监听音讯是否发送胜利?如 ack 胜利音讯,删除 DB 中此音讯。

如果 nack 不胜利的音讯,这个能够依据本身的业务抉择是否重发此音讯。也能够删除此音讯,由本人的业务决定。

如何防止音讯反复生产

幂等性也就是雷同条件下对一个业务的操作,不论操作多少次,后果都是一样。

反复生产的问题?

导致反复生产的起因可能呈现在生产者,也可能呈现在 MQ 或 消费者。

这里说的反复生产问题是指同一个数据被执行了两次,不单单指 MQ 中一条音讯被生产了两次,也可能是 MQ 中存在两条截然不同的生产。

  • 生产者:生产者可能会反复推送一条数据到 MQ 中,为什么会呈现这种状况呢?兴许是一个 Controller 接口被反复调用了 2 次,没有做接口幂等性导致的;也可能是推送音讯到 MQ 时响应比较慢,生产者的重试机制导致再次推送了一次音讯。
  • MQ:在消费者生产完一条数据响应 ack 信号生产胜利时,MQ 忽然挂了,导致 MQ 认为消费者还未生产该条数据,MQ 复原后再次推送了该条音讯,导致了反复生产。
  • 消费者:消费者曾经生产完了一条音讯,正筹备然而还未给 MQ 发送 ack 信号时,此时消费者挂了,服务重启后 MQ 认为消费者还没有生产该音讯,再次推送了该条音讯。

如何保障幂等性?

消费者怎么解决反复生产问题呢?这里提供两种办法:

  • 状态判断法:消费者生产数据后把生产数据记录在 redis 中,下次生产时先到 redis 中查看是否存在该音讯,存在则示意音讯曾经生产过,间接抛弃音讯。
  • 业务判断法:通常数据生产后都须要插入到数据库中,应用数据库的唯一性束缚避免反复生产。每次生产间接尝试插入数据,如果提醒唯一性字段反复,则间接失落音讯。个别都是通过这个业务判断的办法就能够简略高效地防止音讯的反复解决了。
退出移动版