RabbitMQ:伪延时队列
ps:伪延时队列先卖个关子,我们先了解下延时队列。
一、什么是延时队列
所谓延时队列是指消息push到队列后,监听的消费者不能第一时间获取消息,需要等到指定时间才能消费。
一般在业务里面需要对某些消息做定时发送,不想走定时任务或者是用户下单之后多长时间自动失效类似的场景可以考虑通过延时队列实现。
二、RabbitMQ实现
MQ本身并不支持直接的延时队列实现,但是我们可以通过RabbitMQ的消息TTL和Dead Letter规则来实现
Time TO Live (TTL):
RabbitMQ可以针对Queue设置x-expires 或者 针对Message设置 x-message-ttl,来控制消息的生存时间Dead Letter 死信
RabbitMQ官网这样定义死信消息:
- . 消息被拒绝(basic.reject或basic.nack)并且requeue=false.
- . 消息TTL过期
- 队列达到最大长度(队列满了,无法再添加数据到mq中)
- Dead Letter Exchanges(DLX)死信交换机
MQ默认的死信消息是丢弃的,但是我们可以通过设置以下两个属性让死信消息转发到我们指定的队列。
- x-dead-letter-exchange:出现dead letter之后将dead letter重新发送到指定exchange
- x-dead-letter-routing-key:出现dead letter之后将dead letter重新按照指定的routing-key发送
延时队列实现:
了解了MQ队列的TTL和Dead Letter之后,我们就可以通过这两个特性来实现,首先我们通过设置消息或者队列的TTL来设置消息在指定时间后成为死信,再设置死信消息的路由转发规则到特定队列,消费者通过监听这个特定队列就能实现延时队列的效果。代码实现
生产者发送消息:ttlQueue存放过期时间的队列,deadLetterQueue死信转发队列,seconds是过期时间
public static void sendTTLMsg(String ttlQueue, String deadLetterQueue, Object msg, Integer seconds) {
MqSender.getInstance().setHost(RABBIT_MQ_HOST);
// 获取到连接以及MQ通道
Connection connection;
try {
connection = MqSender.getInstance().newConnection();
// 从连接中创建通道
Channel channel = connection.createChannel();
// 配置
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", "");
args.put("x-dead-letter-routing-key", deadLetterQueue);
channel.queueDeclare(deadLetterQueue, true, false, false, null);
channel.queueDeclare(ttlQueue, true, false, false, args);
// 发送消息
channel.basicPublish("", ttlQueue, new AMQP.BasicProperties.Builder().expiration(String.valueOf(seconds)).build(), MAPPER.writeValueAsBytes(msg));
channel.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
消费者通过监听deadLetterQueue来实现延时消息监听
三、 延时队列的问题
通过我们测试发现,这种方式实现的延时队列,在队列设置TTL的情况下是可以正常的,但是如果根据消息设置了不同的TTL,就会有问题,因为MQ本质上还是消息队列中间件,队列是遵循先进先出的,如果有两个消息先后入队,但是后入队的消息TTL小于前面的消息,它必须等待之前的消息被消费完后才能挪到队列头部,这样不同延时消息就会出现问题。
通过RabbitMQ官网的文档也介绍了这个问题:
Only when expired messages reach the head of a queue will they actually be discarded (or dead-lettered)
所以我才称之为MQ的伪延时队列,这种延时队列在消息TTL不同的情况下并不能实现真正的延时消费。
四、解决RabbitMQ的伪延时方案
既然RabbitMQ无法支持不同TTL消息的延时消费,那么如果我们要实现这种功能,有什么方案呢,在实际业务开发中,我们有这样的解决方案:
首先我们会创建多级延时消费队列(比如两分钟,三十分钟,一天三种,具体可以根据业务量和访问量还有时间精确度来划分,这里的两分钟、三十分钟是指队列统一的TTL),push消费队列的时候,会根据需要延时的时间,丢到不同的消费队列,比如小于三十分钟的我们push到两分钟队列,三十分钟到一天的放入三十分钟队列,超过一天的放入一天队列,在死信队列的监听器做同样的判断,如果是小于等于当前时间消息的,立马消费,否则按照上述规则继续循环到不同的延时队列
这种方案解决了多级延时消费的问题,并且能够较大程度地避免了消息的重复循环,降低MQ的压力,但是缺点也比较明显,因为最低是两分钟的延时,理论上来说最多会有两分钟的误差,如果对时间要求性比较高的,可以适当调低最低一级别的延时TTL,比如一分钟或者三十秒
类似代码如下:cts是需要消费掉的时间戳
long now = System.currentTimeMillis();
long cts = Long.valueOf(feedComment.getCts());
if (cts - now <= 30 * 60 * 1000) {
MqSender.sendTTLMsg(MqConstants.FEED_COMMENT_DELAY_QUEUE_2MIN, MqConstants.FEED_COMMENT_AUTO_POST_QUEUE, feedComment, 2 * 60);
} else if (cts - now <= 24 * 60 * 60 * 1000) {
MqSender.sendTTLMsg(MqConstants.FEED_COMMENT_DELAY_QUEUE_30MIN, MqConstants.FEED_COMMENT_AUTO_POST_QUEUE, feedComment, 30 * 60);
} else {
MqSender.sendTTLMsg(MqConstants.FEED_COMMENT_DELAY_QUEUE_24HOUR, MqConstants.FEED_COMMENT_AUTO_POST_QUEUE, feedComment, 24 * 60 * 60);
}
RabbitMQ:伪延时队列的更多相关文章
- 基于rabbitMQ 消息延时队列方案 模拟电商超时未支付订单处理场景
前言 传统处理超时订单 采取定时任务轮训数据库订单,并且批量处理.其弊端也是显而易见的:对服务器.数据库性会有很大的要求,并且当处理大量订单起来会很力不从心,而且实时性也不是特别好 当然传统的手法还可 ...
- rabbitMq实现延时队列
原文:https://my.oschina.net/u/3266761/blog/1926588 rabbitMq是受欢迎的消息中间件之一,相比其他的消息中间件,具有高并发的特性(天生具备高并发高可用 ...
- RabbitMq 实现延时队列-Springboot版本
rabbitmq本身没有实现延时队列,但是可以通过死信队列机制,自己实现延时队列: 原理:当队列中的消息超时成为死信后,会把消息死信重新发送到配置好的交换机中,然后分发到真实的消费队列: 步骤: 1. ...
- 【日常摘要】- RabbitMq实现延时队列
简介 什么是延时队列? 一种带有延迟功能的消息队列 过程: 使用场景 比如存在某个业务场景 发起一个订单,但是处于未支付的状态?如何及时的关闭订单并退还库存? 如何定期检查处于退款订单是否已经成功退款 ...
- rabbitmq实现延时队列(死信队列)
基于队列和基于消息的TTL TTL是time to live 的简称,顾名思义指的是消息的存活时间.rabbitMq可以从两种维度设置消息过期时间,分别是队列和消息本身. 队列消息过期时间-Per-Q ...
- Rabbitmq的延时队列的使用
配置: spring: rabbitmq: addresses: connection-timeout: username: guest password: guest publisher-confi ...
- RabbitMQ及延时队列
一.简介 我用过RabbirMQ的发布订阅模式,以及一对一的延迟队列. 1.RabbitMQ的有消息确认机制,消费一条则队列中少一条,也有对应的消费到消息及认为是消费成功这样的模式,一般使用前者. 发 ...
- rabbitmq 安装延时队列插件rabbitmq-delayed-message-exchange
1.下载rabbitmq-delayed-message-exchange(注意版本对应) 链接:https://github.com/rabbitmq/rabbitmq-delayed-messag ...
- IOS IAP 自动续订 之 利用rabbitmq延时队列自动轮询检查是否续订成功
启用针对自动续期订阅的服务器通知: - 官方地址: - https://help.apple.com/app-store-connect/#/dev0067a330b - 相关字段, 相关类型地址: ...
随机推荐
- IP释放、更新、以及清除DNS
此篇文章会带您进行DNS释放.更新以及清除步骤,并协助您就解决您网络方面问题.请在下方选择您所使用的操作系统. Windows 8 在桌面按住Windows + X按键. 选择Command Prom ...
- 孔浩老师的 Struts2 教程听课笔记(思维导图)
最近有空重头学习了一遍孔浩老师的 Struts2 教程,重新写了一份听课笔记.后面常用 form 标签.服务器端验证.异常处理因为时间问题,没有来得及整理.后续我会抽空补上.最近忙着准备笔试.面试. ...
- 电脑如何连接windows server服务器
第一步:win+r 启动‘运行’,输入mstsc 第二步:连接远程桌面,输入服务器ip 第三步,输入账户密码,登陆即可. 注释:mstsc,全称 Microsoft Telnet Screen Con ...
- Jmeter 中文乱码解决方案
中文乱码一直都是比较让人棘手的问题,我们在使用Jmeter的过程中,也会遇到中文乱码问题,我查阅网上的资料解决方案都大同小异,而且不够全面或者不够详细,经过潜心研究终有所得,现在我将jmeter乱码解 ...
- APT攻防整理-常用CVE
常用CVE CVE-- 受影响版本 Windows /Windows /Windows Server /Windows Server R2/Windows Server /Windows Server ...
- SAP选择屏幕开发(一)(转)
原文链接:https://blog.csdn.net/wtxhai/article/details/90632686 用户通过屏幕操作来实现与SAP的数据交互,而SAP的屏幕开发一般分为两种,一种是通 ...
- Pytest+Allure2+Jenkins搭建
前置: (1)安装Python3(这里版本为3.7) (2)搭建Jenkins环境 一.安装pytest 直接使用pip安装(这里由于笔者的环境同时安装了Python2和Python3,所以在pyth ...
- LR为什么用极大似然估计,损失函数为什么是log损失函数(交叉熵)
首先,逻辑回归是一个概率模型,不管x取什么值,最后模型的输出也是固定在(0,1)之间,这样就可以代表x取某个值时y是1的概率 这里边的参数就是θ,我们估计参数的时候常用的就是极大似然估计,为什么呢?可 ...
- mysql中插入中文时显示乱码
在插入mysql的时候参数是中文的时候显示的是???乱码,这个是由于没有配置字符编码引起的 只要在SqlMapconfig.xml文件中加上<property name="url&qu ...
- 【Python—参数】*arg与**kwargs参数的用法
在python中,这两个是python中的可变参数,*arg表示任意多个无名参数,类型为tuple;**kwargs表示关键字参数,为dict. # *允许你传入0个或任意个参数,这些可变参数在函数调 ...