第一种方式 利用两个特性 Time to live (TTL) Dead Letter Exchanges (DLX) 来实现 也就是通过死信队列来完成
先把死信的队列 死信的交换机还有普通队列都定义出来 交换机与队列之间的关系绑定在代码中也是有注释说明
示例代码
创建死信队列在 rabbitMQ 的配置类
package com.aisile.rabbitmq.rabbitconfig;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QueueDLX {
//todo 死信队列名称
public static final String QUEUEDLX_NAME = "queuedlx_name";
//todo 死信交换机名称
public static final String EXCHANGEDLX_NAME = "exchangedlx_name";
//todo 普通队列名称
public static final String QUEUE_NAME = "queue_name";
//todo 死信队列绑定死信交换机
@Bean(QUEUEDLX_NAME)
public Queue queueDLX() {
return QueueBuilder.durable(QUEUEDLX_NAME)
//#todo 这里是死信队列的特有特性,
//#todo 具有该特性才可以进行转发给交换机
//#todo "x-dead-letter-exchange" 这个参数是固定写死的
.withArgument("x-dead-letter-exchange", EXCHANGEDLX_NAME)
//#todo "x-dead-letter-routing-key" 这个参数也是固定写死的
.withArgument("x-dead-letter-routing-key", EXCHANGEDLX_NAME).build();
}
//todo 死信交换机
@Bean(EXCHANGEDLX_NAME)
public DirectExchange EXCHANGEDLX_NAME(){
return new DirectExchange(EXCHANGEDLX_NAME);
}
//todo 普通队列
@Bean(QUEUE_NAME)
public Queue QUEUE_NAME(){
return new Queue(QUEUE_NAME,true);
}
//todo 死信交换机绑定普通队列
@Bean
public Binding bindingDLX(
@Qualifier(EXCHANGEDLX_NAME) DirectExchange directExchange,
@Qualifier(QUEUE_NAME)Queue queue){
return BindingBuilder.bind(queue).to(directExchange).with(EXCHANGEDLX_NAME);
}
}
提供者发送消息
public void sendTTLDLX(){
System.out.println(new Date());
String str = "{'id' :'2'}";
//发送消息给死信队列
rabbitTemplate.convertAndSend(QueueDLX.QUEUEDLX_NAME, str.getBytes(), new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//存活时间 毫秒 2分钟
message.getMessageProperties().setExpiration("1000");
return message;
}
});
}
消费者监听消息代码
package com.aisile.rabbitmq.listener;
import com.aisile.rabbitmq.rabbitconfig.QueueDLX;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.util.Date;
@Component
@RabbitListener(queues = QueueDLX.QUEUE_NAME)
public class CumMQListenerCJ {
@RabbitHandler
public void msg(@Payload Object msg) throws UnsupportedEncodingException {
System.out.println(new Date());
Message message = (Message) msg;
String str = new String(message.getBody(), "utf-8");
System.out.println(str);
}
}
死信队列实现延时队列流程图
总结
死信队列 说白了也是一个队列 队列的规则就是 先进先出后进后出 比如说先进来的死信消息过期时间是 5 秒 后进的消息过期时间是 3 秒 第二个消息过期了 得等到第一个消息过期了之后才会被消费
第二种延迟队列(Delay Queue)模式
安装插件
RabbitMQ的官方也推出了一个插件 原生支持延迟队列效果
1. 下载插件 (可以在插件列表中下载)
下载后之后将插件上传到服务器中的一个位置 (自己要记得传到哪了)
2.复制插件到docker容器的rabbitmq容器的插件文件夹下
docker cp /root/rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez 容器id:/plugins
3.进入容器 查看是否拷贝成功 并启动插件
docker exec -it rabbitmq bash
4. 启动插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
进入 rabbitMQ 页面 查看有相应的交换机类型 即可
实例代码
配置类
package com.aisile.rabbitmq.rabbitconfig;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QueueCJ {
//交换机 ctrl+shift+u 将大写字母转换成小写
public static final String CJ_EXCHANGEDLX = "cj_exchangedlx";
//普通队列
public static final String CJ_QUEUE = "cj_queue";
//声明消息队列名称
@Bean(CJ_QUEUE)
public Queue MESSAGE_QUEUE(){
return new Queue(CJ_QUEUE,true);
}
//声明 DirectExchange 交换机
@Bean(CJ_EXCHANGEDLX)
public DirectExchange CJ_EXCHANGEDLX(){
return (DirectExchange)
//与普通交换机唯一的不同就是 .delayed() 声明它是一个延迟交换机
ExchangeBuilder.directExchange(CJ_EXCHANGEDLX).delayed().durable(true).build();
}
/**
* #todo 绑定交换机和消息队列
* @param directExchange
* @param queue
* @return
*/
@Bean
public Binding DLX_EXCHANGE_MESSAGE_QUEUE(
@Qualifier(CJ_EXCHANGEDLX)DirectExchange directExchange,
@Qualifier(CJ_QUEUE)Queue queue
){
return BindingBuilder.bind(queue).to(directExchange).with(CJ_EXCHANGEDLX);
}
}
提供者(发消息的)
public void sendTTLDLX() throws UnsupportedEncodingException {
System.out.println(new Date());
String str = "{'id':'888'}";
Message message = MessageBuilder.withBody(str.getBytes("utf-8"))
.setHeader("x-delay", 10000).build();
rabbitTemplate.convertAndSend(QueueCJ.CJ_EXCHANGEDLX,QueueCJ.CJ_EXCHANGEDLX,message);
}
消费者(接受消息的)
package com.aisile.rabbitmq.listener;
import com.aisile.rabbitmq.rabbitconfig.QueueCJ;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.util.Date;
@Component
@RabbitListener(queues = QueueCJ.CJ_QUEUE)
public class CumMQListenerCJ {
@RabbitHandler
public void msg(@Payload Object msg) throws UnsupportedEncodingException {
System.out.println(new Date());
Message message = (Message) msg;
String str = new String(message.getBody(), "utf-8");
System.out.println(str);
}
}
总结
这个插件可以解决死信队列的先进先出限制的问题 没有了死信队列 直接是将消息发送给交换机的 该交换机可以将消息进行保存等待死亡时间到了 直接路由给队列 消费者就可以监听到消息了进行消费了