一、实现原理
队列ttl+死信exchange
简述:使用两个队列,一个队列接收消息不消费,等待指定时间后消息死亡,再由该队列绑定的死信exchange再次将其路由到另一个队列提供业务消费。
ttl:x-message-ttl 消息存活时间
二、实现
- 声明交换机和队列
@SpringBootConfiguration
public class RabbitDelayConfig {
/**
* 死信队列
*/
public static final String DEAD_QUEUE = "dead.queue";
/**
* 延时队列
*/
public static final String DELAY_QUEUE = "delay.queue";
/**
* 死信交换机
*/
public static final String DEAD_EXCHANGE = "dead.exchange";
/**
* 原交换机
*/
public static final String DELAY_EXCHANGE = "delay.exchange";
/**
* 声明延时队列(生产者投递消息)
*
*
* @return
*/
@Bean
public Queue getDelayQueue() {
//参数一:队列名称;参数二:队列中消息存活时间(单位ms);参数三:消息过期转发的死信交换机;参数四:死信队列和死信交换机绑定的rootingKey
Queue queue = QueueBuilder.durable(DELAY_QUEUE).ttl(10 * 1000).deadLetterExchange(DEAD_EXCHANGE).deadLetterRoutingKey("deadKey").build();
return queue;
}
/**
* 声明死信队列(接收过期转发消息)
*
* @return
*/
@Bean
public Queue getDeadQueue() {
return new Queue(DEAD_QUEUE, true);
}
/**
* 死信交换机(转发消息到死信队列)
*
* @return
*/
@Bean
public DirectExchange deadExchange() {
DirectExchange exchange = new DirectExchange(DEAD_EXCHANGE, true, false);
return exchange;
}
/**
* 原交换机(投递消息到延时队列)
*
* @return
*/
@Bean
public DirectExchange delayExchange() {
DirectExchange exchange = new DirectExchange(DELAY_EXCHANGE, true, false);
return exchange;
}
/**
* 绑定死信队列和死信交换机
*
* @return
*/
@Bean
public Binding bindDeadExchangeQueue() {
Binding binding = BindingBuilder.bind(getDeadQueue()).to(deadExchange()).with("deadKey");
return binding;
}
/**
* 绑定延时队列和原交换机
*
* @return
*/
@Bean
public Binding bindDelayExchangeQueue() {
Binding binding = BindingBuilder.bind(getDelayQueue()).to(delayExchange()).with("delay-routingKey");
return binding;
}
}
- 创建生产者
@SpringBootTest
@RunWith(SpringRunner.class)
public class DelayMqTest {
@Resource
private RabbitTemplate rabbitTemplate;
@Test
public void send() {
rabbitTemplate.convertAndSend(RabbitDelayConfig.DELAY_EXCHANGE, "delay-routingKey", "延时消息");
System.out.println("发送消息时间:" + System.currentTimeMillis());
}
- 创建消费者(监听的队列是死信队列)
@Component
public class DelayConsumer {
@RabbitListener(queues = RabbitDelayConfig.DEAD_QUEUE)
public void listenDead1(String message) {
System.out.println("接收时间:" + System.currentTimeMillis());
System.out.println("消费者一接收消息:" + message);
}
}
结果:
可以看到接收消息的时间和发送消息的时间和我们设置的消息存活时间基本一致;
三、另外
设置消息存活时间有两种方式:
1.创建延时队列时设置,这里设置之后,所以到该队列中的消息都有相同的存活时间:
@Bean
public Queue getDelayQueue() {
Queue queue = QueueBuilder.durable(DELAY_QUEUE).ttl(10 * 1000).deadLetterExchange(DEAD_EXCHANGE).deadLetterRoutingKey("deadKey").build();
return queue;
}
2.发送消息时设置,这里设置之后,所以到该队列中的消息都有不同的存活时间:
@Test
public void send1() {
rabbitTemplate.convertAndSend(RabbitDelayConfig.DELAY_EXCHANGE, "delay-routingKey", "延时消息1111", message -> {
message.getMessageProperties().setExpiration(20 * 1000 + "");
return message;
});
System.out.println("发送消息时间:" + System.currentTimeMillis());
}
特别的,如果两者均设置的消息存活时间,则消息的真正存活时间是发送消息时指定的;如上代码则消息真正存活的时间是20000ms;