zl程序教程

您现在的位置是:首页 >  其他

当前栏目

关于RabbitMQ实现延迟消息之大家避而不谈的坑望周知

2023-04-18 16:12:56 时间

一. 背景

我之前在做延迟消息的时候做了很多的尝试,也摒弃了很多的方案,其中就有RabbitMQ死信队列和延迟插件的使用,其实他们都有比较严重的局限性,但是这两天我在看博客时候发现呢,很多文章或者公众号大肆宣扬它的功能点,丝毫不提它的坑,甚至夸大其词说啥"拼多多百亿消息的实现",我觉得这样真的不太好,很容易误导别人,写扫盲文章点出来它的优点和缺点最好.滥用不研究很容易出现生产问题

二. 我现在描述下自己调查基于RabbitMQ两种方式实现延迟消息的局限性

2.1死信队列局限性

我们基于TTL和死信队列(概念性的解释和demo可以看我之前的博客)做到延迟消息的局限性在于,延迟粒度是在队列级别的,我们需要对队列设置固定的TTL,然后每个消息在进入队列时候拥有统一的初始化消息消亡时间,如果我们想要做到延迟时间随机必须要做到创建很多很多很多不同TTL的延迟队列.

2.2基于RabbitMQ延迟插件的局限性

由于运维那边没有预装RabbitMQ延迟插件,因此我想的是先来一波压测证明它各方面没问题再麻烦运维去装,这波测试果然发现了问题. 关于下面延迟插件概念性的解释和demo可以看我之前的博客

2.2.1 我这里反过来,先说下缺点

我们在第一次使用这个延迟插件的时候做了一个压测,压测结果显示大约100W数据量的延迟消息会导致内存和Cpu使用量的急速上升.可以想象如果大家开始几万测试消息用的很爽,然后直接上生产,几百W消息进来一下就把整个RabbitMQ搞挂掉了.

问题出在那里呢? 由于RabbitMQ是使用erlang语言开发的,源码确实看不懂,查了一些文档没搞明白后(实际上就没发现有人提到这些坑),去了官网看了下,这里总结下自己的发现

  • RabbitMQ延迟插件其内部基于改良版定时器实现,所以会占用大量的CPU用于运算
  • 由于其内部是没有存储消息的进入时间,只有一个TTL,其在重启的时候很容易造成延迟时间的重置
  • 延迟插件作者并没有实现比较完善的延迟消息存储和扫描,因此我们不建议实现大量消息的存储.

关于作者对于RabbitMQ延迟插件这个半成品的解释,大家可以看下面的链接 https://github.com/rabbitmq/rabbitmq-delayed-message-exchange https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/issues/72

2.2.2 优点

延迟粒度是消息级别的,非常方便,在数据量比较小的情况下,使用它是没问题的,或者自己可以做一些额外的配合,尽量使其延时最近的数据,并且数据量维持到一个比较低的程度,这点的实现方式的话,我这里大概有一点自己思路,大家可以稍微参考下.

  • 第一 监听延迟交换机的延迟消息量,是其尽量维持在一个可控的量级(比如10W)
  • 第二 持久化延迟时间比较久的消息,然后只延迟最近几小时的消息,然后去扫持久化延迟消息表,但是这点比较坑,因为有些消息可能比较集中的某个时刻发,还是可能会出现问题.
  • 第三 两者结合,同意持久化消息进入Mysql,即时扫描监听延迟队列积压消息量,在保障RabbitMQ没有安全性问题的情况下进行扫描持久化消息表,将消息扫进MQ

三. 一点小建议

一定要擦亮自己的双眼啊,很多作者为了吸睛各种不负责任标题去吸引人,大家一定要自己验证,一定要基于自己生产的规模做必要的压测,比如拉去自己生产的流量高峰去做吞吐量压测或者并发压测,最好处理能力达到自己生产的三倍以上,我这里也有个压测工具Jmeter的使用科普,大家可以看看