一、延迟任务的使用场景
1、下单成功,30分钟未支付。支付超时,自动取消订单
2、订单签收,签收后7天未进行评价。订单超时未评价,系统默认好评
3、下单成功,商家5分钟未接单,订单取消
4、配送超时,推送短信提醒
5、三天会员试用期,三天到期后准时准点通知用户,试用产品到期了
......
对于延时比较长的场景、实时性不高的场景,我们可以采用任务调度的方式定时轮询处理。如:xxl-job。
今天我们讲解延迟队列的实现方式,而延迟队列有很多种实现方式,普遍会采用如下等方式,如:
- 1.如基于RabbitMQ的队列ttl+死信路由策略:通过设置一个队列的超时未消费时间,配合死信路由策略,到达时间未消费后,回会将此消息路由到指定队列
- 2.基于RabbitMQ延迟队列插件(rabbitmq-delayed-message-exchange):发送消息时通过在请求头添加延时参数(headers.put("x-delay", 5000))即可达到延迟队列的效果。(顺便说一句阿里云的收费版rabbitMQ当前可支持一天以内的延迟消息),局限性:目前该插件的当前设计并不真正适合包含大量延迟消息(例如数十万或数百万)的场景,详情参见 #/issues/72 另外该插件的一个可变性来源是依赖于 Erlang 计时器,在系统中使用了一定数量的长时间计时器之后,它们开始争用调度程序资源。
- 3.使用redis的zset有序性,轮询zset中的每个元素,到点后将内容迁移至待消费的队列,(redisson已有实现)
- 4.使用redis的key的过期通知策略,设置一个key的过期时间为延迟时间,过期后通知客户端(此方式依赖redis过期检查机制key多后延迟会比较严重;Redis的pubsub不会被持久化,服务器宕机就会被丢弃)。
二、组件安装
安装rabbitMQ需要依赖erlang语言环境,所以需要我们下载erlang的环境安装程序。网上有很多安装教程,这里不再贴图累述,需要注意的是:该延迟插件支持的版本匹配。
插件Git官方地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange
当你成功安装好插件后运行起rabbitmq管理后台,在新建exchange里就可以看到type类型中多出了这个选项
三、RabbitMQ延迟队列插件的延迟队列实现
1、基本原理
通过 x-delayed-message 声明的交换机,它的消息在发布之后不会立即进入队列,先将消息保存至 Mnesia(一个分布式数据库管理系统,适合于电信和其它需要持续运行和具备软实时特性的 Erlang 应用。目前资料介绍的不是很多)
这个插件将会尝试确认消息是否过期,首先要确保消息的延迟范围是 Delay > 0, Delay =< ?ERL_MAX_T(在 Erlang 中可以被设置的范围为 (2^32)-1 毫秒),如果消息过期通过 x-delayed-type 类型标记的交换机投递至目标队列,整个消息的投递过程也就完成了。
2、核心组件开发走起
引入maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
application.yml简单配置
rabbitmq:
host: localhost
port: 5672
virtual-host: /
RabbitMqConfig配置文件
package com.example.code.bot_monomer.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import