RabbitMQ 延时消息的实现(上)
我们在实际业务中有一些需要延时发送消息的场景,例如:
- 家里有一台智能热水器,需要在30分钟后启动
- 未付款的订单,15分钟后关闭
注意这里的场景是延时,不是定时。当然,解决了延时,定时就很简单了(定时=当前时刻+间隔时间)。
由于RabbitMQ本身不支持延时队列(延时消息),所以要通过其他方式来实现。总的来说有三种:
- 先存储到数据库,用定时任务扫描,登记时刻+延时时间,就是需要投递的时刻
- 利用RabbitMQ的死信队列(Dead Letter Queue)实现
- 利用rabbitmq-delayed-message-exchange插件
定时任务实现比较简单,此处略过。我们来看一下后两种方案分别怎么实现。
前提知识:我们可以在发送消息时指定单条消息的存活时间(Time To Live,TTL)。也可以设置一个队列的消息过期时间。
这两种方式,当队列中的消息到达过期时间(比如30分钟)仍未被消费,就会被发送到队列的死信交换机(Dead Letter Exchange,DLX),被再次路由,此时再次路由到的队列就被称为死信队列(Dead Letter Queue)。需要注意,死信交换机和死信交换机都是基于其用途来描述的,它们实际上也是普通的交换机和普通的队列。如果队列没有指定DLX或者无法被路由到一个DLQ,则队列中过期的消息会被直接丢弃。
因此,我们可以利用消息TTL的特性,实现消息的延时投递。
1、设置单条消息的过期时间的方法:
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2) // 持久化消息
.contentEncoding("UTF-8")
.expiration("10000") // TTL,10秒后没有被消费则被发送到DLX
.build();
channel.basicPublish("", "TEST_TTL_QUEUE", properties, msg.getBytes()); //此处发送到 AMQP Default 这个默认的Direct类型的交换机,并路由到TEST_TTL_QUEUE队列
2、设置队列的消息过期时间的方法:
Map<String, Object> argss = new HashMap<String, Object>();
argss.put("x-message-ttl",6000); // TTL,6秒后没有被消费则被发送到DLX
channel.queueDeclare("TEST_TTL_QUEUE", false, false, false, argss);
注意:如果同时设置了消息的过期时间和队列的消息过期时间,则会取其中一个较小的值。比如消息设置5秒过期,队列设置消息10秒过期,则实际过期时间是5秒。
基于消息TTL,我们来看一下如何利用死信队列(DLQ)实现延时队列:
总体步骤:
1)创建一个交换机
2018已经过去过去,2019还想一成不变吗?拥抱变化,突破瓶颈,想要学习Java架构技术的朋友可以加我的群:725219329,群内每晚都会有阿里技术大牛讲解的最新Java架构技术。并会录制录播视频分享在群公告中,作为给广大朋友的加群的福利——分布式(Dubbo、Redis、RabbitMQ、N