RabbitMQ 延时消息设计
问题背景
- 所谓"延时消息"是指当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费。
- 场景一:客户A在十二点下了一个订单,我想半个小时后来检查一下这个订单的付款状态,根据付款状态来作下一步的处理。 a. 针对场景一,建议采用方案数据库保存+schedule的方式也许更合适。
- 场景二:mdc系统更新了一个A信息,我要通知给A门店信息发生了变化,通知他们回调API来读取最新的值。
如果拿到消息后立即回调,可能因为mdc事务、缓存、从库延迟等原因,拿到变化前的信息,所以mdc希望能延迟一段时间再来消费此消息。
目标
- 可以实现消息按照自定义的时间延迟发送。
- 最好做到对消息生产者和消费者透明,不修改现有应用程序。
总体方案
3.1. 实现原理
- AMQP和RabbitMQ本身没有直接支持延迟队列功能,但是可以通过以下特性模拟出延迟队列的功能。
- RabbitMQ可以针对Queue和Message设置 x-message-ttl,来控制消息的生存时间,如果超时,则消息变为dead letter
- RabbitMQ的Queue可以配置x-dead-letter-exchange 和 x-dead-letter-routing-key(可选)两个参数,用来控制队列内出现了dead letter,则按照这两个参数重新路由。
结合以上两个特性,就可以模拟出延迟消息的功能
参考资料:
- https://www.cloudamqp.com/docs/delayed-messages.html
- http://www.rabbitmq.com/ttl.html
- http://www.rabbitmq.com/dlx.html
- http://www.rabbitmq.com/maxlength.html
3.2. 技术方案
|
方案一:针对Queue设置延迟时间 |
方案二:针对Message设置延迟时间 |
||
|
方案关键点 |
针对需要延迟的Queue配置ttl参数 |
针对Queue设置最大延迟时间 |
|
|
1. 优点: |
发送方对每个Message设置有效延迟时间 |
||
|
a. 维护简单 |
|||
|
b. 客户端完全透明 |
1. 优点 |
||
|
c. 针对每个延迟时间建立一个延迟队 |
a. 发送时可以自定义延迟时间 |
||
|
列 |
2. 缺点 |
||
|
2. 缺点: |
a. 需要升级客户端,对客户端不透明 |
||
|
a. 发送方无法自定义延迟时间 |
b. 需要针对每个队列建立不同的延迟 |
||
|
b. 延迟时间在建Queue时确定,修改 |
队列 |
||
|
不便 |
c. 带延迟参数的send方法容易误用, |
||
|
c. 修改延迟时间需要在MQ集群重新进 |
很难发现 |
||
|
行配置 |
|||
|
选择结果 |
最终选择了方案一 |
3.3. 消息发送流程
3.3.1. 原有业务流程

3.3.2. 自产自消的延迟消息流程

3.3.3. 普通的延迟消息流程

4. 操作步骤
4.1. 建立 delay.exchange

注意事项:
1. 不要设置为Internal,否则将无法接受dead letter
4.2. 建立延时队列(delay queue)

注意事项:
- 按照延期时间配置queue的名字,建议命名规则为delay.{time}.queue,使用delay前缀方便排序和做一些权限控制
- cos线上集群默认配置了delay.1m.queue、delay.5m.queue、delay.15m.queue三个队列
- 通过TTL设置队列的延期时间,对应不同的Queue
- MAX length为最大的积压的消息个数,推荐设置为100w~500w之间,推测方法见积压测试结果,
- 超过MAX length限制以后,队列头部的消息会立即转到delay.exchange进行投递,不会造成消息丢失
- 设置dead letter exchange为刚才配置的 delay.exchange
注意不要配置"dead letter routing key"否则会覆盖掉消息发送时携带的routingkey,导致后面无法路由
4.3. 配置延时路由规则
4.3.1. 需要延时的消息到exchange后先路由到指定的延时队列

4.3.2. delay.exchange 再重新把消息路由到正常的queue或exchang中

4.3.3. 消费者和以前一样从正常queue中接收消费消息
5. 积压消息测试结果
5.1. 积压测试结论
从实现原理上看,对于持久化消息,内存主要保存的是消息的索引数据,从测试结果也可以验证,可以得出以下数据:
- 内存占用方面估算
- 消息占用内存的大小确实和消息体本身大小无关,和消息个数直接相关。
- 消息体为40字节的字符串,积压10万消息,占用101MB内存,23万消息,占用230MB内存
- 消息体为一个整数,积压7万消息,占用64MB内存,24万消息,占用240MB内存
- 一个消息约占1KB的内存,以10GB内存,留一半余量:5GB/1K=500 0000
- 线上单个queue推荐线上的max length不要超过500万,根据延迟时间和消息量来调整此值的大小。
- 消息占用内存的大小确实和消息体本身大小无关,和消息个数直接相关。
- 消息量估算
- 以30分钟延迟,每秒发送1000个消息为例,则最大值为:30*60*1000=180 0000
- 倒推:以500万,30分钟延迟为例,则每秒最多发送的消息个数为:5000000/30/60=2777
- 消息积压数超过max length以后,消息不会丢失,只会导致消息会被提前消费。
结论:1. MAX length推荐这个值设置为100万~500万,既可以满足业务需求,又不超过内存限制
5.2. 40字节消息积压26万

5.3. 4字节消息积压7万

5.4. 4字节消息积压24万

5.5. 4字节消息积压31万

5.6. 4字节消息积压64万

总结:经过压测及基础配置,延时消息可以满足目前的需求,实现消息的延迟处理。
不知道延迟队列加上惰性队列的组合,即可以减小内存占用,又可以实现消息的延迟处理,也是一个非常好的方案。蛋糕和咖啡真的很配。
RabbitMQ 延时消息设计的更多相关文章
- RabbitMQ 延时消息队列
消息延时在日常随处可见: 1.订单创建10min之后不发起支付,自动取消. 2.30min定时推送一次邮件信息. 最常用到方式后台定时任务轮训,量小的时候可以使用,量大会出现数据读取会性能问题.Rab ...
- rabbitMq延时消息分级别
做支付平台的时候.需要实现接受上游支付消息,通知给下游渠道. 针对下游渠道:要实现 按通知次数 递进 延时通知 下游渠道的支付/签约/代扣的状态 可参考微信按照 15/15/30/180/1800/1 ...
- springboot项目整合rabbitMq涉及消息的发送确认,消息的消费确认机制,延时队列的实现
1.引入maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactI ...
- spring boot Rabbitmq集成,延时消息队列实现
本篇主要记录Spring boot 集成Rabbitmq,分为两部分, 第一部分为创建普通消息队列, 第二部分为延时消息队列实现: spring boot提供对mq消息队列支持amqp相关包,引入即可 ...
- RabbitMq(7)消息延时推送
应用场景 目前常见的应用软件都有消息的延迟推送的影子,应用也极为广泛,例如: 淘宝七天自动确认收货.在我们签收商品后,物流系统会在七天后延时发送一个消息给支付系统,通知支付系统将款打给商家,这个过程持 ...
- RabbitMQ实现延时消息的两种方法
目录 RabbitMQ实现延时消息的两种方法 1.死信队列 1.1消息什么时候变为死信(dead-letter) 1.2死信队列的原理 1.3 代码实现 1.4死信队列的一个小坑 2 .延时插件 2. ...
- SpringBoot | 第三十八章:基于RabbitMQ实现消息延迟队列方案
前言 前段时间在编写通用的消息通知服务时,由于需要实现类似通知失败时,需要延后几分钟再次进行发送,进行多次尝试后,进入定时发送机制.此机制,在原先对接银联支付时,银联的异步通知也是类似的,在第一次通知 ...
- Spring boot实战项目整合阿里云RocketMQ (非开源版)消息队列实现发送普通消息,延时消息 --附代码
一.为什么选择RocketMQ消息队列? 首先RocketMQ是阿里巴巴自研出来的,也已开源.其性能和稳定性从双11就能看出来,借用阿里的一句官方介绍:历年双 11 购物狂欢节零点千万级 TPS.万亿 ...
- C#调用RabbitMQ实现消息队列
前言 我在刚接触使用中间件的时候,发现,中间件的使用并不是最难的,反而是中间件的下载,安装,配置才是最难的. 所以,这篇文章我们从头开始学习RabbitMq,真正的从头开始. 关于消息队列 其实消息队 ...
随机推荐
- 关键字提取算法TF-IDF和TextRank(python3)————实现TF-IDF并jieba中的TF-IDF对比,使用jieba中的实现TextRank
关键词: TF-IDF实现.TextRank.jieba.关键词提取数据来源: 语料数据来自搜狐新闻2012年6月—7月期间国内,国际,体育,社会,娱乐等18个频道的新闻数据 数据处 ...
- [20170909]为什么是12秒.txt
[20170909]为什么是12秒.txt --//在开发程序时我一般会强调开发尽量不要写一些自定义函数,往往可能导致CPU忙.--//例子很像这样: CREATE OR REPLACE FUNCTI ...
- sysfs_create_group创建sysfs接口
在调试驱动,可能需要对驱动里的某些变量进行读写,或函数调用.可通过sysfs接口创建驱动对应的属性,使得可以在用户空间通过sysfs接口的show和store函数与硬件交互: Syss接口可通过sys ...
- inode 耗尽
背景: 之前为了提升大硬盘mkfs文件系统的速度,使用了大文件选项 mkfs.ext4 -T largefile /dev/xvde1 没有仔细算能存多少文件,结果今天发现磁盘没用完,但inode用完 ...
- Docker容器学习与分享01
1.什么是容器? 容器技术是一种虚拟化的方案,与传统的虚拟机不同,传统的虚拟机是通过中间层将一台或多台独立的机器虚拟运行于物理硬件之上,而容器是直接运行在操作系统内核之上的用户空间. 所以容器虚拟化又 ...
- 三叔学FPGA系列之二:Cyclone V中的POR、配置、初始化,以及复位
对于FPGA内部的复位,之前一直比较迷,这两天仔细研究官方数据手册,解开了心中的诸多疑惑,感觉自己又进步了呢..... 原创不易,转载请转原文,注明出处,谢谢. 一.关于POR(Power-On ...
- [零基础学python]为什么要开设本栏目
这个栏目的名称叫做"零基础学Python". 如今网上已经有不少学习python的课程.当中也不乏精品.按理说,不缺少我这个基础类型的课程了.可是,我注意到一个问题.无论是课程还是 ...
- 解决jqueryeasyUI dialog 弹出窗体超出浏览器,导致不能关闭的bug
使用panel的onMove事件攻克了panel,dialog以及window组件在被拖动时,会超出浏览器边界而无法拖回的情况. 当窗体被拖出浏览器有边界时.$(document).width();会 ...
- Android实践--apk反编译
版权声明:本文为博主原创文章.转载请注明出处. https://blog.csdn.net/ysjian_pingcx/article/details/25468867 Android apk反编译一 ...
- IOS HTML5页面中数字自动变蓝并识别为手机号
开发HTML5的项目时发现页面元素为一串数字时,IOS手机会默认显示成蓝色字体,并且添加下划线,点击数字时会提示是否识别为手机号. 解决此问题的方法很简单,在head标签中添加下面的meta标记即可解 ...