RabbitMQ高级
一、过期时间TTL(Time-To-Live)
过期时间TTL表示可以对消息设置预期的时间,在这个时间段内都可以被消费者获取,过期则自动删除(过期的称为dead message被投递到死信队列)
1、通过队列属性设置,队列中所有消息都有相同的过期时间
2、对消息进行单独设置,每条消息TTL可以不同
如果两种方法一起使用就看谁的TTL比较短,以短的为准
1、设置队列属性
通过队列属性设置消息TTL的方法是在queue.declare方法中加入x-message-ttl参数,单位 为ms.
//声明队列
@Bean("itemQueue")
public Queue itemQueue(){
Map<String,Object> map = new HashMap<>();
//设置队列的过期时间
map.put("x-message-ttl",6000);
return new Queue("itemQueue",true,false,false,map);
}
会看见队列有个TTL
2、单独设置消息
二、死信队列
死信队列DLX:当业务处理失败(比如抛异常并且达到了retry的上限),就会将消息重新投递到另一个Exchange(Dead Letter Exchanges),该Exchange再根据routingkey重定向到另一个队列,在这个队列重新处理该消息。
来自一个队列的消息可以被当做‘死信’,即被重新发不到另一个“exchange”去,这样的情况有
1、消息被拒绝(
2、消息过期TTL
3、队列长度达到最大
代码
application.yml
spring:
# rabbitMQ配置
rabbitmq:
host: 39.97.123.123
port: 5672
virtual-host: my_vhost #虚拟主机(相当于mysql的数据库库名)
username: admin
password: admin
# 死信队列必要配置
listener:
simple:
retry:
enabled: true #允许消息消费失败重试
max-attempts: 3 #消息消费最多允许3次
initial-interval: 1000 #消息多次消费间隔1s
default-requeue-rejected: false #设置为false,会丢弃消息或者重新发布到死信队列
RabbitDeadLetterConfig
package com.jinv.studentinfo.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: jinv
* @CreateTime: 2020-06-12 21:53
* @Description: RabbitMQ死信队列配置类
*/
@Configuration
public class RabbitDeadLetterConfig {
public static final String DEAD_LETTER_EXCHANGE = "DLT_EXCHANGE";
public static final String DEAD_LETTER_QUEUE="DLT_QUEUE";
public static final String DEAD_LETTER_TEST_ROUTING_KEY="DLT_TEST_KEY";
public static final String DEAD_LETTER_REDIRECT_QUEUE="DLT_REDIRECT_QUEUE";
public static final String DEAD_LETTER_REDIRECT_ROUTING_KEY="DLT_REDIRECT_KEY";
/**
* 声明死信交换机
* 死信交换机对于交换机类型没有特殊要求,不一定要directExchange,可以为其他类型的
*/
@Bean("deadLetterExchange")
public Exchange deadLetterExchange(){
return ExchangeBuilder.directExchange(DEAD_LETTER_EXCHANGE).durable(true).build();
}
/**
* 声明死信队列
*/
@Bean("deadLetterQueue")
public Queue deadLetterQueue(){
Map<String,Object> args = new HashMap<>();
//x-dead-letter-exchange 声明死信交换机
args.put("x-dead-letter-exchange",DEAD_LETTER_EXCHANGE);
//x-dead-letter-routing-key 声明死信队列抛出异常重定向队列的routingkey(DLT_REDIRECT_KEY)
args.put("x-dead-letter-routing-key",DEAD_LETTER_REDIRECT_ROUTING_KEY);
return QueueBuilder.durable(DEAD_LETTER_QUEUE).withArguments(args).build();
}
/**
* 声明重定向队列
*/
@Bean("redirectQueue")
public Queue redirectQueue(){
return QueueBuilder.durable(DEAD_LETTER_REDIRECT_QUEUE).build();
}
/**
* 死信队列绑定到死信交换机上
*/
@Bean
public Binding deadLetterQueueBinding(){
return new Binding(
DEAD_LETTER_QUEUE,
Binding.DestinationType.QUEUE,
DEAD_LETTER_EXCHANGE,
DEAD_LETTER_TEST_ROUTING_KEY,
null);
}
/**
* 重定向队列通过RoutinKey绑定到死信队列上
*/
@Bean
public Binding deadLetterRedirectQueueBinding(){
return new Binding(
DEAD_LETTER_REDIRECT_QUEUE,
Binding.DestinationType.QUEUE,
DEAD_LETTER_EXCHANGE,
DEAD_LETTER_REDIRECT_ROUTING_KEY,
null);
}
}
生产者
RabbitMQController
@GetMapping("/testDeadLetter")
@ResponseBody
public String testDeadLetter(@RequestParam String msg){
/**
* 发送消息
* 参数1:交换机名称
* 参数2:路由key
* 参数3:发送的消息
*/
rabbitTemplate.convertAndSend(RabbitDeadLetterConfig.DEAD_LETTER_EXCHANGE,
RabbitDeadLetterConfig.DEAD_LETTER_TEST_ROUTING_KEY,msg);
return "成功向死信交换机发送消息!";
}
死信队列消费者
/**
* 死信队列消费者
* 这里会抛出异常
* @param msg:消息
*/
@RabbitListener(queues= RabbitDeadLetterConfig.DEAD_LETTER_QUEUE)
public void deadLetterConsumer(String msg){
System.out.println("======正在尝试消费死信队列消息====");
int i=1/0;//自行弄出异常
System.out.println("消费者了消费:"+msg);
}
/**
* 重定向队列消费者
*/
@RabbitListener(queues = RabbitDeadLetterConfig.DEAD_LETTER_REDIRECT_QUEUE)
public void deadLetterRedirectConsumer(String msg){
System.out.println("重定向消费者消费了消息:"+msg);
}
测试
三、延迟队列
消息的TTL+死信Exchange结合=延迟队列
1、消息的TTL——消息的存货时间(通过设置消息的expiration字段或者x-message-ttl属性)。rabbitMQ可以设置队列的TTL和消息的TTL,队列TTL指的是该队列所有消息的存活时间,消息的TTL指的是单条消息的,所以一条消息在不同队列可以有不同的TTL,以小的为准。
2、Dead Letter Exchanges死信交换机
(1)一个消息被Consumer拒收了,并且reject方法的参数里是requeue是false。也就是说不会被再次放在队列里,被其他消费者使用
(2)上面的消息TTL到了,消息过期了
(3)队列的长度限制满了,排在前面的消息会被丢弃或者扔到死信路由上
代码
application.yml
spring:
# rabbitMQ配置
rabbitmq:
host: 39.97.123.123
port: 5672
virtual-host: my_vhost #虚拟主机(相当于mysql的数据库库名)
username: admin
password: admin
# 死信队列必要配置
listener:
simple:
retry:
enabled: true #允许消息消费失败重试
max-attempts: 3 #消息消费最多允许3次
initial-interval: 1000 #消息多次消费间隔1s
default-requeue-rejected: false #设置为false,会丢弃消息或者重新发布到死信队列
RabbitDelayLetterConfig
package com.jinv.studentinfo.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: jinv
* @CreateTime: 2020-06-14 00:51
* @Description: 延迟队列配置
*/
@Configuration
public class RabbitDelayLetterConfig {
/**
* 延时交换机——交换机用于重新分配队列(接收死信队列中的过期消息,将其转发到需要延迟消息的模块队列)
*/
@Bean("delayExchange")
public DirectExchange exchange(){
return new DirectExchange("X-DELAY-EXCHANGE");
}
/**
* 实际消费队列
* 用于延时消费的队列
*/
@Bean("repeatTradeQueue")
public Queue repeatTradeQueue(){
return new Queue("X-RECIEVE-QUEUE",true,false,false,null);
}
/**
* 绑定交换机兵指定routingkey(死信队列绑定延迟交换机和实际消费队列绑定延迟交换机的routingkey一直)
*/
@Bean
public Binding repeatTradeBinding(@Qualifier("repeatTradeQueue") Queue queue,
@Qualifier("delayExchange") DirectExchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("delay");
}
/**
* 死信队列
*/
@Bean
public Queue delayLetterQueue(){
Map<String,Object> args = new HashMap<>();
//设置延迟时间
args.put("x-message-ttl",1000*60);
//设置绑定延时交换机(这里的绑定和上面的binding是有区别的,这里的绑定在15672客户端是看不到的)
args.put("x-dead-letter-exchange","X-DELAY-EXCHANGE");
//设置队列消息的routingkey
args.put("x-dead-letter-routing-key","delay");
return new Queue("X-DELAY-QUEUE",true,false,false,args);
}
}
生产者
RabbitController
@GetMapping("/testDelayLetter")
@ResponseBody
public String testDealyLetter(@RequestParam String msg){
//这里是给延迟队列发送消息,不需要事先配置routingkey,
//消息进入延迟队列会自动获取队列赋予的routingkey
rabbitTemplate.convertAndSend("X-DELAY-QUEUE","我要发送延迟消息");
System.out.println("发送时间:"+LocalDateTime.now());
return "已经发送延迟消息";
}
消费者
Mylistener
/**
* 延迟队列消费者
*/
@RabbitListener(queues = "X-RECIEVE-QUEUE")
public void delayLetterConsumer(String msg){
System.out.println("实际接收信息时间:"+LocalDateTime.now());
System.out.println("消费者接收到的信息:"+msg);
}
测试结果(延迟一分钟)
四、消息确认机制
确认并且保证消息被送达:发布确认和事务支持(两者不可同时使用)在channel为事务时,不可引入确认模式,同样channel为确认模式下,不支持事务
1、发布确认
(1)消息发送成功确认
(2)消息发送失败回调
2、事务支持
五、消息追踪
有待补充高级应用和集群部署