注:供个人查阅学习使用,不做其他用途
实现方式:
声明一个TTL交换机,创建队列的时候绑定一个死信交换机,发送消息时往正常队列里放入一条具有超时时间的消息,costomer不对消息进行读取,消息就会进去指定的死信交换机,在costomer绑定死信交换机和死信队列,并进行监听,就可以读取到超时的消息。(也就是自己设置的延迟发送的时间)
准备工作:
rabbitmq下载网上有具体的方法,这里暂不详解
pom文件
<!--消息队列中间件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.5.8</version>
</dependency>
相关配置文件:根据实际情况进行修改
spring.rabbitmq.host=192.168.31.154
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=admin123
spring.rabbitmq.virtual-host=/
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=10000ms
spring.rabbitmq.template.retry.max-interval=300000ms
spring.rabbitmq.template.retry.multiplier=2
spring.rabbitmq.template.exchange=topic.exchange
一、producer创建一个TTL交换机
注:之所以要动态的创建队列,是因为队列遵循先进先出,即使后面的数据过期了也会等前面的数据过期或者被拿走才会进入到死信队列,所以一个TTL队列最好就只遵循一个超时时间
@Configuration
public class TTLMqConfig {
public static final String QUEUE_EMAIL = "ttl.queue";//队列名称
public static final String EXCHANGE_NAME="ttl.direct";//交换机名称
public static final String ROUTINGKEY_EMAIL="dl";
public static final String DL_EXCHANGE_NAME="dl.ttl.direct";//死信交换机名称
public static final String DL_QUEUE_NAME="dl.ttl.queue";//死信队列名称
/***
* 声明交换机
* @param: []
* @return: org.springframework.amqp.core.DirectExchange
* @author: kevin
* @date: 2023/3/8 15:20
*/
@Bean(EXCHANGE_NAME)
public DirectExchange ttlDirectExchange(){
return new DirectExchange(EXCHANGE_NAME);
}
/***
* 注入rabbitMq admin对象 不然直接引用会空指针
* @param: [connectionFactory]
* @return: org.springframework.amqp.rabbit.core.RabbitAdmin
* @author: kevin
* @date: 2023/3/9 10:46
*/
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
// 只有设置为 true,spring 才会加载 RabbitAdmin 这个类
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
/***
* 创建队列 目前需要动态创建队列,所以就注释掉这个方法
* @param:
* @return:
* @author: kevin
* @date: 2023/3/9 10:20
*/
// @Bean
// public Queue ttlQueue(){
// return QueueBuilder
// //指定队列名称,并持久化
// .durable(QUEUE_EMAIL)
// //设置队列的超时时间,10秒
// //直接在发消息那里设置超时时间
.ttl(10000)
// //指定死信交换机
// .deadLetterExchange(DL_EXCHANGE_NAME)
// //设置RoutingKey
// .deadLetterRoutingKey(ROUTINGKEY_EMAIL)
// .build();
// }
// @Bean
// public Binding ttlBinding(){
// return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
// }
}
二、发消息时创建一个队列并绑定死信交换机
// 创建两分钟的消息+队列
String queue = "ttl.queue" + 120000;
Message message = MessageBuilder
.withBody("两分钟的消息".getBytes(StandardCharsets.UTF_8))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT)
//设置延迟时间
.setExpiration("120000")
.build();
MqUtil.createQueueIfExist(queue);
// String message = "测试发送";
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
// 发送消息
rabbitTemplate.convertAndSend("ttl.direct", queue, message,correlationData);
工具类方法
@Component
public class MqUtil {
public static final String EXCHANGE_NAME="ttl.direct";//交换机名称
private static RabbitAdmin rabbitAdmin;
//死信队列的key
public static final String ROUTINGKEY_DL="dl";
//普通交换机和队列的key
public static final String ROUTINGKEY_EMAIL="ttl";
public static final String DL_EXCHANGE_NAME="dl.ttl.direct";//死信交换机名称
public static final String DL_QUEUE_NAME="dl.ttl.queue";//死信队列名称
@Resource
public void setRabbitAdmin(RabbitAdmin rabbitAdmin) {
MqUtil.rabbitAdmin = rabbitAdmin;
}
private static DirectExchange exchange;
@Resource
public void setExchange(@Qualifier(EXCHANGE_NAME) DirectExchange exchange) {
MqUtil.exchange = exchange;
}
/**
* 建立与RabbitMQ的连接
* @return
* @throws Exception
*/
// public static Connection getConnection() throws Exception {
// //定义连接工厂
// ConnectionFactory factory = new ConnectionFactory();
// //设置服务地址
// factory.setHost("192.168.1.103");
// //端口
// factory.setPort(5672);
// //设置账号信息,用户名、密码、vhost
// factory.setVirtualHost("/kavito");//设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机就相当于一个独立的mq
// factory.setUsername("kavito");
// factory.setPassword("123456");
// // 通过工厂获取连接
// Connection connection = factory.newConnection();
// return connection;
// }
/***
* 如果不存在队列就创建
* @param: []
* @return: boolean
* @author: kevin
* @date: 2023/3/9 10:00
*/
public static void createQueueIfExist(String queueName){
Properties queueProperties = rabbitAdmin.getQueueProperties(queueName);
if (queueProperties==null){
//如果不存在就创建
Queue queue = QueueBuilder
//指定队列名称,并持久化
.durable(queueName)
//设置队列的超时时间,10秒
//直接在发消息那里设置超时时间
// .ttl(10000)
//指定死信交换机
.deadLetterExchange(DL_EXCHANGE_NAME)
//设置RoutingKey
.deadLetterRoutingKey(ROUTINGKEY_DL).build();
//创建队列
rabbitAdmin.declareQueue(queue);
//队列绑定交换机 队列和非死信交换机之间就用队列名字当key
rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(exchange).with(queueName));
}
}
三、customer监听死信队列
注:不要监听正常队列,会直接取走消息
/**
* @description:注解方式声明死信交换机、死信队列
* @author: kevin
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "dl.ttl.queue", durable = "true"),
exchange = @Exchange(name = "dl.ttl.direct"),
key = "dl"
))
public void listenDlQueue(String msg){
log.info("接收到 dl.ttl.queue的延迟消息:{}", msg);
}