问题背景

  1. 所谓"延时消息"是指当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费。
  2. 场景一:客户A在十二点下了一个订单,我想半个小时后来检查一下这个订单的付款状态,根据付款状态来作下一步的处理。 a. 针对场景一,建议采用方案数据库保存+schedule的方式也许更合适。
  3. 场景二:mdc系统更新了一个A信息,我要通知给A门店信息发生了变化,通知他们回调API来读取最新的值。

如果拿到消息后立即回调,可能因为mdc事务、缓存、从库延迟等原因,拿到变化前的信息,所以mdc希望能延迟一段时间再来消费此消息。

目标

  1. 可以实现消息按照自定义的时间延迟发送。
  2. 最好做到对消息生产者和消费者透明,不修改现有应用程序。

总体方案

 3.1. 实现原理

  1. AMQP和RabbitMQ本身没有直接支持延迟队列功能,但是可以通过以下特性模拟出延迟队列的功能。
  2. RabbitMQ可以针对Queue和Message设置 x-message-ttl,来控制消息的生存时间,如果超时,则消息变为dead letter
  3. RabbitMQ的Queue可以配置x-dead-letter-exchange 和 x-dead-letter-routing-key(可选)两个参数,用来控制队列内出现了dead letter,则按照这两个参数重新路由。

结合以上两个特性,就可以模拟出延迟消息的功能

参考资料:

  1. https://www.cloudamqp.com/docs/delayed-messages.html
  2. http://www.rabbitmq.com/ttl.html
  3. http://www.rabbitmq.com/dlx.html
  4. 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)

注意事项:

  1. 按照延期时间配置queue的名字,建议命名规则为delay.{time}.queue,使用delay前缀方便排序和做一些权限控制
  2. cos线上集群默认配置了delay.1m.queue、delay.5m.queue、delay.15m.queue三个队列
  3. 通过TTL设置队列的延期时间,对应不同的Queue
  4. MAX length为最大的积压的消息个数,推荐设置为100w~500w之间,推测方法见积压测试结果,
  5. 超过MAX length限制以后,队列头部的消息会立即转到delay.exchange进行投递,不会造成消息丢失
  6. 设置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. 积压测试结论

从实现原理上看,对于持久化消息,内存主要保存的是消息的索引数据,从测试结果也可以验证,可以得出以下数据:

  1. 内存占用方面估算
    1. 消息占用内存的大小确实和消息体本身大小无关,和消息个数直接相关。
      1. 消息体为40字节的字符串,积压10万消息,占用101MB内存,23万消息,占用230MB内存
      2. 消息体为一个整数,积压7万消息,占用64MB内存,24万消息,占用240MB内存
    2. 一个消息约占1KB的内存,以10GB内存,留一半余量:5GB/1K=500 0000
    3. 线上单个queue推荐线上的max length不要超过500万,根据延迟时间和消息量来调整此值的大小。
  2. 消息量估算
    1. 以30分钟延迟,每秒发送1000个消息为例,则最大值为:30*60*1000=180 0000
    2. 倒推:以500万,30分钟延迟为例,则每秒最多发送的消息个数为:5000000/30/60=2777
  3. 消息积压数超过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 延时消息设计的更多相关文章

  1. RabbitMQ 延时消息队列

    消息延时在日常随处可见: 1.订单创建10min之后不发起支付,自动取消. 2.30min定时推送一次邮件信息. 最常用到方式后台定时任务轮训,量小的时候可以使用,量大会出现数据读取会性能问题.Rab ...

  2. rabbitMq延时消息分级别

    做支付平台的时候.需要实现接受上游支付消息,通知给下游渠道. 针对下游渠道:要实现 按通知次数 递进 延时通知 下游渠道的支付/签约/代扣的状态 可参考微信按照 15/15/30/180/1800/1 ...

  3. springboot项目整合rabbitMq涉及消息的发送确认,消息的消费确认机制,延时队列的实现

    1.引入maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactI ...

  4. spring boot Rabbitmq集成,延时消息队列实现

    本篇主要记录Spring boot 集成Rabbitmq,分为两部分, 第一部分为创建普通消息队列, 第二部分为延时消息队列实现: spring boot提供对mq消息队列支持amqp相关包,引入即可 ...

  5. RabbitMq(7)消息延时推送

    应用场景 目前常见的应用软件都有消息的延迟推送的影子,应用也极为广泛,例如: 淘宝七天自动确认收货.在我们签收商品后,物流系统会在七天后延时发送一个消息给支付系统,通知支付系统将款打给商家,这个过程持 ...

  6. RabbitMQ实现延时消息的两种方法

    目录 RabbitMQ实现延时消息的两种方法 1.死信队列 1.1消息什么时候变为死信(dead-letter) 1.2死信队列的原理 1.3 代码实现 1.4死信队列的一个小坑 2 .延时插件 2. ...

  7. SpringBoot | 第三十八章:基于RabbitMQ实现消息延迟队列方案

    前言 前段时间在编写通用的消息通知服务时,由于需要实现类似通知失败时,需要延后几分钟再次进行发送,进行多次尝试后,进入定时发送机制.此机制,在原先对接银联支付时,银联的异步通知也是类似的,在第一次通知 ...

  8. Spring boot实战项目整合阿里云RocketMQ (非开源版)消息队列实现发送普通消息,延时消息 --附代码

    一.为什么选择RocketMQ消息队列? 首先RocketMQ是阿里巴巴自研出来的,也已开源.其性能和稳定性从双11就能看出来,借用阿里的一句官方介绍:历年双 11 购物狂欢节零点千万级 TPS.万亿 ...

  9. C#调用RabbitMQ实现消息队列

    前言 我在刚接触使用中间件的时候,发现,中间件的使用并不是最难的,反而是中间件的下载,安装,配置才是最难的. 所以,这篇文章我们从头开始学习RabbitMq,真正的从头开始. 关于消息队列 其实消息队 ...

随机推荐

  1. maven(四):一个基本maven项目的pom.xml配置

    继续之前创建的test项目,一个基本项目的pom.xml文件,通常至少有三个部分 第一部分,项目坐标,信息描述等 <modelVersion>4.0.0</modelVersion& ...

  2. 洗礼灵魂,修炼python(59)--爬虫篇—httplib模块

    httplib 1.简介 同样的,httplib默认存在于python2,python3不存在: httplib是python中http协议的客户端实现,可以用来与 HTTP 服务器进行交互,支持HT ...

  3. 洗礼灵魂,修炼python(9)--灵性的字符串

    python几大核心之——字符串 1.什么是字符串 其实前面说到数据类型时说过了,就是带有引号的参数,“”引号内的一切东西就是字符串,字符串又叫文本. 2.创建字符串的两种方式: 3.字符串的方法: ...

  4. 【第三篇】SAP ABAP7.5x新语法之程序结构&SubScreen

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文地址:SAP ABAP7.5x系列之程序结构& ...

  5. Postgre SQL连接服务器失败

    首先这是登陆postgre sql时提示的错误信息: psql: 无法联接到服务器: Connection refused (0x0000274D/10061)        服务器是否在主机 &qu ...

  6. JavaScript 中的匿名函数((function() {})();)与变量的作用域

    以前都是直接用前端框架Bootstrap,突然想看看Javascript,发现javascript是个非常有趣的东西,这里把刚碰到的一个小问题的理解做下笔录(废话不多说,上代码). /** * Exa ...

  7. RESTframework简介

    什么是RESTful? RESTful是一种开发理念,REST是Roy Thomas Fileding在他博文提出的.REST特点;url简洁,将参数通过url传递到服务器,简单就是说URL定位资源, ...

  8. git 一些基本的命令操作总结

    配置远程仓库:git remote add test http://xxxx/tool/device_management.git test为远程仓库别名克隆远程仓库到本地:git clone htt ...

  9. jquery的自定义事件通过on绑定trigger触发

    jquery绑定自定义事件,可以实现预先绑定好一个处理方法,当需要使用的时候利用jquery trigger来触发自定义事件,以达到方便快捷的目的.我们来假设一个这样的场景,一个textarea中的字 ...

  10. 网络流(四)dinic算法

    传送门: 网络流(一)基础知识篇 网络流(二)最大流的增广路算法 网络流(三)最大流最小割定理 网络流(四)dinic算法 网络流(五)有上下限的最大流 网络流(六)最小费用最大流问题 转自:http ...