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,真正的从头开始. 关于消息队列 其实消息队 ...
随机推荐
- maven(四):一个基本maven项目的pom.xml配置
继续之前创建的test项目,一个基本项目的pom.xml文件,通常至少有三个部分 第一部分,项目坐标,信息描述等 <modelVersion>4.0.0</modelVersion& ...
- 洗礼灵魂,修炼python(59)--爬虫篇—httplib模块
httplib 1.简介 同样的,httplib默认存在于python2,python3不存在: httplib是python中http协议的客户端实现,可以用来与 HTTP 服务器进行交互,支持HT ...
- 洗礼灵魂,修炼python(9)--灵性的字符串
python几大核心之——字符串 1.什么是字符串 其实前面说到数据类型时说过了,就是带有引号的参数,“”引号内的一切东西就是字符串,字符串又叫文本. 2.创建字符串的两种方式: 3.字符串的方法: ...
- 【第三篇】SAP ABAP7.5x新语法之程序结构&SubScreen
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文地址:SAP ABAP7.5x系列之程序结构& ...
- Postgre SQL连接服务器失败
首先这是登陆postgre sql时提示的错误信息: psql: 无法联接到服务器: Connection refused (0x0000274D/10061) 服务器是否在主机 &qu ...
- JavaScript 中的匿名函数((function() {})();)与变量的作用域
以前都是直接用前端框架Bootstrap,突然想看看Javascript,发现javascript是个非常有趣的东西,这里把刚碰到的一个小问题的理解做下笔录(废话不多说,上代码). /** * Exa ...
- RESTframework简介
什么是RESTful? RESTful是一种开发理念,REST是Roy Thomas Fileding在他博文提出的.REST特点;url简洁,将参数通过url传递到服务器,简单就是说URL定位资源, ...
- git 一些基本的命令操作总结
配置远程仓库:git remote add test http://xxxx/tool/device_management.git test为远程仓库别名克隆远程仓库到本地:git clone htt ...
- jquery的自定义事件通过on绑定trigger触发
jquery绑定自定义事件,可以实现预先绑定好一个处理方法,当需要使用的时候利用jquery trigger来触发自定义事件,以达到方便快捷的目的.我们来假设一个这样的场景,一个textarea中的字 ...
- 网络流(四)dinic算法
传送门: 网络流(一)基础知识篇 网络流(二)最大流的增广路算法 网络流(三)最大流最小割定理 网络流(四)dinic算法 网络流(五)有上下限的最大流 网络流(六)最小费用最大流问题 转自:http ...