RabbitMQ延迟队列
- 什么是延时队列
- 延时队列的使用场景
- RabbitMQ实现延时队列
- 1. 利用TTL DLX实现延时队列
- 2. 利用插件(rabbitmq_delayed_message_exchange)实现延时队列
- 1. 引入依赖
- 2. 创建配置类
- 3. 消费端代码
- 4. 生产端代码
什么是延时队列
延时队列是用于存放需要在指定时间被处理的元素的队列,简单来说,就是放置在该队列里面的消息是不需要立即消费的,而是等待一段时间之后取出消费。
延时队列的使用场景
- 订单在三十分钟之内未支付则自动取消;
- 用户发起退款,如果一定时间内没有处理,则发送通知给相关人员;
- 预定会议后,需要再会议前三十分钟通知与会人员;
- …
这些场景都有一个特点,需要在某个事件发生之后或者之前的指定时间点完成某一项任务。
RabbitMQ实现延时队列
RabbitMQ 实现延时队列一般而言有两种方式:
- 利用两个特性: Time To Live(TTL)、Dead Letter Exchange(DLX)
- 利用 RabbitMQ 中的插件 rabbitmq_delayed_message_exchange
1. 利用TTL DLX实现延时队列
- Time To Live(TTL):RabbitMQ 可以针对 Queue 设置 x-expires 或者 针对 Message 设置 x-message-ttl,来控制消息的生存时间,如果超时(两者同时设置以最小时间为准),则消息变为dead letter(死信)。
- Dead Letter Exchanges(DLX):通过为队列添加 x-dead-letter-exchange 属性,指定死信交换机,还可通过 x-dead-letter-routing-key 属性配置需要路由到死信队列的Routing key。如果队列内出现了dead letter,则按照这两个参数重新路由转发到指定的队列。
2. 利用插件(rabbitmq_delayed_message_exchange)实现延时队列
插件下载地址:https://www.rabbitmq.com/community-plugins.html
注意下载插件要和安装的rabbitmq版本一致
下载解压后,得到一个 .ez 的压缩文件,找到 rabbitmq 安装目录下的 plugins,将解压的文件复制到该目录下。输入命令启动该插件:
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
然后重启 RabbitMQ。
1. 引入依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.3</version>
</dependency>
2. 创建配置类
public class RabbitMQConfig {
private RabbitMQConfig() {
}
// 主机IP
public static final String HOST = "127.0.0.1";
// 主机port
public static final Integer PORT = 5672;
// 主机port
public static final String VHOST = "/";
// 主机port
public static final String USERNAME = "admin";
// 主机port
public static final String PASSWORD = "admin";
// 交换机名称
public static final String DIRECT_EXCHANGE = "delayed_direct_exchange";
// 队列名称
public static final String QUEUE_NAME = "delayed_direct_queue";
// Routing key
public static final String ROUTING_KEY = "kiss";
}
3. 消费端代码
import com.rabbitmq.client.*;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
/**
* 消费者
*/
public class MessageConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
// 设置主机IP
factory.setHost(RabbitMQConfig.HOST);
// 设置端口
factory.setPort(RabbitMQConfig.PORT);
// 设置 Vhost
factory.setVirtualHost(RabbitMQConfig.VHOST);
// 设置访问用户
factory.setUsername(RabbitMQConfig.USERNAME);
factory.setPassword(RabbitMQConfig.PASSWORD);
// 建立连接
Connection connection = factory.newConnection();
// 创建Channel消息通道
Channel channel = connection.createChannel();
// 声明 x-delayed-message 类型的Exchange
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-delayed-type", "direct");
channel.exchangeDeclare(RabbitMQConfig.DIRECT_EXCHANGE, "x-delayed-message", false, false, arguments);
/**
* 声明队列,参数String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
* String queue:指定队列名称
* boolean durable:是否持久化
* boolean exclusive:是否排他,既是否创建者私有,如果为true,会对当前队列加锁,其他通道不能访问,并且
* 在连接关闭时会自动删除,不受持久化和自动删除限制
* boolean autoDelete:是否自动删除
* Map<String, Object> arguments:其他参数
*/
channel.queueDeclare(RabbitMQConfig.QUEUE_NAME, false, false, false, null);
/**
* 绑定交换和队列,参数String queue, String exchange, String routingKey, Map<String, Object> arguments
*/
channel.queueBind(RabbitMQConfig.QUEUE_NAME, RabbitMQConfig.DIRECT_EXCHANGE, RabbitMQConfig.ROUTING_KEY, null);
// 创建消费者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// 获取消息
String msg = new String(body, "UTF-8");
System.out.println("message:" + msg + " 消息接收时间" +
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));
}
};
// 开始获取消息String queue, boolean autoAck, Consumer callback
channel.basicConsume(RabbitMQConfig.QUEUE_NAME, true, consumer);
}
}
4. 生产端代码
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 生产者
*/
public class MessageProvider {
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory();
// 设置主机IP
factory.setHost(RabbitMQConfig.HOST);
// 设置端口
factory.setPort(RabbitMQConfig.PORT);
// 设置 Vhost
factory.setVirtualHost(RabbitMQConfig.VHOST);
// 设置访问用户
factory.setUsername(RabbitMQConfig.USERNAME);
factory.setPassword(RabbitMQConfig.PASSWORD);
try (
// 建立连接
Connection connection = factory.newConnection();
// 创建Channel消息通道
Channel channel = connection.createChannel();
) {
Map<String, Object> headers = new HashMap<>();
headers.put("x-delay", 1000);// 延迟的时间间隔
// 设置属性,消息5秒后过期
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.headers(headers)
.build();
String msg = "阁下可是常山赵子龙,发送时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
channel.basicPublish(RabbitMQConfig.DIRECT_EXCHANGE, RabbitMQConfig.ROUTING_KEY, properties, msg.getBytes("UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
}
}