rabbitmq专栏 代码地址:码云
rabbitmq实现延迟消息
- 1. 什么是延迟队列?
- 2. 延迟队列使用场景
- 3. 延迟队列的实现方式
- 3.1 利用TTL+死信队列
- 3.2 利用RabbitMQ插件实现
- 3.2.1 容器挂载
- 3.2.2 上传插件
- 3.2.3 复制到RabbitMQ的插件目录
- 3.2.4 使插件生效
- 4. 代码实现
- 4.1 RabbitMQConfig配置
- 4.2 producer
- 4.3 consumer
- 4.4 启动服务,测试
1. 什么是延迟队列?
延时队列,首先,它是一种队列,队列意味着内部的元素是有序的,元素出队和入队是有方向性的,元素从一端进入,从另一端取出。
其次,延时队列,最重要的特性就体现在它的延时属性上,跟普通的队列不一样的是,普通队列中的元素总是等着希望被早点取出处理,而延时队列中的元素则是希望被在指定时间得到取出和处理,所以延时队列中的元素是都是带时间属性的,通常来说是需要被处理的消息或者任务。
简单来说,延时队列就是用来存放需要在指定时间被处理的元素的队列。
2. 延迟队列使用场景
那么什么时候需要用延时队列呢?考虑一下以下场景:
- 订单在十分钟之内未支付则自动取消。
- 新创建的店铺,如果在十天内都没有上传过商品,则自动发送消息提醒。
- 账单在一周内未支付,则自动结算。
- 用户注册成功后,如果三天内没有登陆则进行短信提醒。
- 用户发起退款,如果三天内没有得到处理则通知相关运营人员。
- 预定会议后,需要在预定的时间点前十分钟通知各个与会人员参加会议。
3. 延迟队列的实现方式
3.1 利用TTL+死信队列
生产者生产一条延时消息,根据需要延时时间的不同,利用不同的routingkey将消息路由到不同的延时队列,每个队列都设置了不同的TTL属性,并绑定在同一个死信交换机中,消息过期后,根据routingkey的不同,又会被路由到不同的死信队列中,消费者只需要监听对应的死信队列进行处理即可。
这种方式的弊端,无法做到通用性,每搞一个新的延迟任务,都要去实现一个实现的TTL+死信队列,比较麻烦;
3.2 利用RabbitMQ插件实现
安装一个插件即可:Community Plugins
下载rabbitmq_delayed_message_exchange插件,然后解压放置到RabbitMQ的插件目录。
接下来,进入RabbitMQ的安装目录下的sbin目录,执行下面命令让该插件生效,然后重启RabbitMQ。
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
下面详细操作一下:
3.2.1 容器挂载
本地创建一个rabbitmq目录:
执行下面命令:
docker run -it --rm --name rabbitmq5 -v /home/docker/rabbitmq/:/root/data/ -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 5672:5672 -p 15672:15672 rabbitmq:3-management
3.2.2 上传插件
插件下载地址: rabbitmq-delayed-message-exchange插件下载
然后直接通过finalshell上传rabbitmq_delayed_message_exchange插件到/home/docker/rabbitmq, 插件会自动同步到容器里的/root/data目录里面
3.2.3 复制到RabbitMQ的插件目录
cp /root/data/rabbitmq_delayed_message_exchange-3.8.0.ez /plugins
3.2.4 使插件生效
接下来,进入RabbitMQ的安装目录下的sbin目录,执行下面命令让该插件生效,然后重启RabbitMQ。
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
执行上面这个命令的时候有个报错说是版本不支持,又换了个rabbitmq_delayed_message_exchange-3.9.0.ez的,然后执行成功,如下图:
然后通过 docker restart + 容器id 重启即可。
4. 代码实现
4.1 RabbitMQConfig配置
/**
* delayedDirect交换机名称
*/
public static final String DELAYED_DIRECT_EXCHANGE="delayedDirectExchange";
/**
* delayed direct队列名称
*/
public static final String DELAYED_DIRECT_QUEUE="delayedDirectQueue";
/**
* delayed_direct路由Key
*/
public static final String DELAYED_DIRECT_ROUTINGKEY="delayed_directRoutingKey";
/**
* 定义一个delayed direct交换机
* @return
*/
@Bean
public CustomExchange delayedDirectExchange(){
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange(DELAYED_DIRECT_EXCHANGE,"x-delayed-message", true, false, args);
}
/**
* 定义一个DELAYED direct队列
* @return
*/
@Bean
public Queue delayedDirectQueue(){
return new Queue(DELAYED_DIRECT_QUEUE);
}
/**
* 定义一个队列和交换机的绑定
* @return
*/
@Bean
public Binding delayedDirectBinding(){
return BindingBuilder.bind(delayedDirectQueue()).to(delayedDirectExchange()).with(DELAYED_DIRECT_ROUTINGKEY).noargs();
}
4.2 producer
启动类:
4.3 consumer
另外,把手动签收注释掉,让它自动签收消息:
4.4 启动服务,测试
测试成功!