消息的TTL就是消息的存活时间。
RabbitMQ可以对队列和消息分别设置TTL。对队列设置就是队列中没有被消费的消息的保留时间,也可以对每一个单独的消息做单独的设置。
超过了这个时间,我们认为这个消息就死了,称之为死信。
如果队列设置了,消息也设置了,那么会取小的。
一个消息在满足如下条件下,就成为死信, 会进入死信交换机,记住这里是交换机而不是队列,一个交换机可以对应很多队列。
一个消息被Consumer消费者拒收了,并且reject方法的参数里requeue是false。也就是说不会被再次放在队列里,被其他消费者使用。
上面的消息的TTL到了,消息过期了。
队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信路由上。
Dead Letter Exchange死信交换机其实就是一种普通的exchange交换机,和创建其他exchange交换面没有两样。只是在某一个设置Dead Letter Exchange的队列中有消息过期了,会自动触发消息的转发,发送到Dead Letter Exchange中去。
x-dead-letter-exchange 指向死信交换机
x-dead-letter-routing-key 发送死信时的路由键
x-message-ttl 消息过期时间, 毫秒数
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class RabbitTimeoutConfig {
@Bean
public Queue timeoutDelayQueue(){
Map<String, Object> arguments = new HashMap<>(3);
// 设置死信交换机
arguments.put("x-dead-letter-exchange", "delay-exchange" );
// 设置死信路由键
arguments.put("x-dead-letter-routing-key", "delay.timeout");
// 设置过期时间
arguments.put("x-message-ttl", 60000);
// 队列名称 , 是否为持久性 , 是否为独享 , 是否自动删除, 属性设置
return new Queue("timeout.delay.queue", true, false, false, arguments);
}
@Bean
public Queue baseQueue(){
// 队列名称 , 是否为持久性 , 是否为独享 , 是否自动删除
return new Queue("base.queue", true, false, false, null);
}
@Bean
public Exchange timeoutExchange(){
// 交换机名称 , 是否为持久性 , 是否自动删除, 属性
return new TopicExchange("timeout-exchange", true, false, null);
}
@Bean
public Exchange delayExchange(){
// 交换机名称 , 是否为持久性 , 是否自动删除, 属性
return new TopicExchange("delay-exchange", true, false, null);
}
@Bean
public Binding delayBindingTimeout(){
return new Binding("timeout.delay.queue", Binding.DestinationType.QUEUE, "timeout-exchange" , "test.timeout", null);
}
@Bean
public Binding baseBindingDelay(){
return new Binding("base.queue", Binding.DestinationType.QUEUE, "delay-exchange" , "delay.timeout", null);
}
}
@RequestMapping("/sender/test3")
public String test3(){
String msg = "Mode delay, Rabbit MQ . " + new Date();
String cd = UUID.randomUUID().toString();
CorrelationData correlationData = new CorrelationData(cd);
// 交换机, 路由键, 消息, 消息id
rabbitTemplate.convertAndSend("timeout-exchange","test.timeout", msg , correlationData);
return msg;
}
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class TimeoutService {
@RabbitListener(queues = "base.queue")
@RabbitHandler
public void timeoutListener(Message message){
System.out.println("message = " + message);
System.out.println( " time = " + new Date());
}
}
只使用一个交接机来实现
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class RabbitTimeoutConfig {
@Bean
public Queue baseQueue(){
// 队列名称 , 是否为持久性 , 是否为独享 , 是否自动删除
return new Queue("base.queue", true, false, false, null);
}
@Bean
public Queue newDelayQueue(){
Map<String, Object> arguments = new HashMap<>(3);
// 设置死信交换机
arguments.put("x-dead-letter-exchange", "timeout-delay-exchange" );
// 设置死信路由键
arguments.put("x-dead-letter-routing-key", "new.delay.timeout");
// 设置过期时间
arguments.put("x-message-ttl", 60000);
// 队列名称 , 是否为持久性 , 是否为独享 , 是否自动删除, 属性设置
return new Queue("new.delay.queue", true, false, false, arguments);
}
@Bean
public Exchange timeoutDelayExchange(){
// 交换机名称 , 是否为持久性 , 是否自动删除, 属性
return new TopicExchange("timeout-delay-exchange", true, false, null);
}
@Bean
public Binding newDelayBindingTimeoutDelay(){
return new Binding("new.delay.queue", Binding.DestinationType.QUEUE, "timeout-delay-exchange" , "new.test.timeout", null);
}
@Bean
public Binding baseBindingTimeoutDelay(){
return new Binding("base.queue", Binding.DestinationType.QUEUE, "timeout-delay-exchange" , "new.delay.timeout", null);
}
}
@RequestMapping("/sender/test4")
public String test4(){
String msg = "Mode delay 2, Rabbit MQ . " + new Date();
String cd = UUID.randomUUID().toString();
CorrelationData correlationData = new CorrelationData(cd);
// 交换机, 路由键, 消息, 消息id
rabbitTemplate.convertAndSend("timeout-delay-exchange","new.test.timeout", msg , correlationData);
return msg;
}
与 实例一 相同
修改 实例二 中, 死信队列 , 注释掉 过期赶时间
@Bean
public Queue newDelayQueue(){
Map<String, Object> arguments = new HashMap<>(3);
// 设置死信交换机
arguments.put("x-dead-letter-exchange", "timeout-delay-exchange" );
// 设置死信路由键
arguments.put("x-dead-letter-routing-key", "new.delay.timeout");
// 设置过期时间
// arguments.put("x-message-ttl", 60000);
// 队列名称 , 是否为持久性 , 是否为独享 , 是否自动删除, 属性设置
return new Queue("new.delay.queue", true, false, false, arguments);
}
@RabbitListener(queues = "new.delay.queue")
@RabbitHandler
public void newTimeoutListener(Message message, Channel channel) throws IOException {
System.out.println("message = " + message);
System.out.println( " time = " + new Date());
long deliveryTag = message.getMessageProperties().getDeliveryTag();
// 拒收消息, 并 不会将信息 放回到队列
channel.basicNack(deliveryTag, false, false);
}
发消息测试, 立刻收到消息
设置 消息的过期时间, 实现死信
注释掉 对死信监听的代码
@RequestMapping("/sender/test5")
public String test5(){
String msg = "Mode 设置消息过期时间, Rabbit MQ . " + new Date();
String cd = UUID.randomUUID().toString();
CorrelationData correlationData = new CorrelationData(cd);
rabbitTemplate.convertAndSend("timeout-delay-exchange","new.test.timeout", msg, (messagePostProcessor) -> {
// 消息设置成 60 秒过期
messagePostProcessor.getMessageProperties().setExpiration("60000");
return messagePostProcessor;
}, correlationData);
return msg;
}
测试 1 分钟后, 会收到消息, 这样过期时间就由 消息来 设置
但 有这样的一个问题
多个不同过期时间消息进入队列, 队列会根据第一个接收到的消息的过期时间来等待
增加新的请求, 分别设置过期时间 为 40 秒, 20 秒
@RequestMapping("/sender/test6")
public String test6(){
String msg = "Mode 设置消息过期时间, Rabbit MQ . " + new Date();
String cd = UUID.randomUUID().toString();
CorrelationData correlationData = new CorrelationData(cd);
rabbitTemplate.convertAndSend("timeout-delay-exchange","new.test.timeout", msg, (messagePostProcessor) -> {
// 消息设置成 40 秒过期
messagePostProcessor.getMessageProperties().setExpiration("40000");
return messagePostProcessor;
}, correlationData);
return msg;
}
@RequestMapping("/sender/test7")
public String test7(){
String msg = "Mode 设置消息过期时间, Rabbit MQ . " + new Date();
String cd = UUID.randomUUID().toString();
CorrelationData correlationData = new CorrelationData(cd);
rabbitTemplate.convertAndSend("timeout-delay-exchange","new.test.timeout", msg, (messagePostProcessor) -> {
// 消息设置成 20 秒过期
messagePostProcessor.getMessageProperties().setExpiration("20000");
return messagePostProcessor;
}, correlationData);
return msg;
}
依次 发出 test5, test6, test7 请求
分别对应的过期时间是 60 秒, 40秒 , 20秒,
但 要等到 60秒, 才收到死信消息, 是因为 第一个 test5 要等待 60 秒