消息的TTL(Time To Live)就是消息的存活时间。RabbitMQ可以对队列和消息分别设置TTL。
对队列设置没有消费者消费的保留时间,也可以对每一个单独的消息做单独的设置。
超过了这个时间,就认为这个消息死了,称之为死信。
如果队列设置了,消息也设置了,那么会取小的。所以一个消息如果被路由到不同的队列中,这个消息的死亡时间有可能不一样(不同的队列设置)。
死信路由DLX(Dead Letter Exchange):一个消息在满足如下条件下,会进死亡路由:
1. 一个消息被consumer拒收了,并不会再次被其他消费者使用
2. 消息的ttl到了,消息过期了
3. 队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信路由上
DLX其实就是一种普通的exchange,和创建其他的exchange没有两样。只是在某一个设置Dead Letter Exchanger的队列中有消息过期了,会自动触发消息的转发,发送到DLX中去。
我们既可以控制消息在一段时间后变成死信,又可以控制消息变成死信,并被路由到某一个指定的交换机,结合二者,其实就可以实现一个延时队列 。
声明死信队列的方式,使用如下参数:
arguments.put("x-dead-letter-exchange", "dlx名称");
arguments.put("x-dead-letter-routing-key", "routingkey");
arguments.put("x-message-ttl", "过期时间");
以定时关闭订单为例:
-
订单创建成功,发送消息到创建订单的路由
-
创建订单的路由转发消息给延时队列,延时队列的延时时间就是订单从创建到支付过程,允许的最大等待时间。延时队列不能有消费者(即消息不能被消费)
-
延时时间一到,消息被转入DLX(死信路由)
-
死信路由把死信消息转发给死信队列
-
订单系统监听死信队列,获取到死信消息后,执行关单解库存操作
配置延时队列:
@EnableRabbit
@Configuration
public class RabbitMqConfig {
/**
* 交换机
* @return
*/
@Bean
public Exchange exchange(){
return new TopicExchange("ORDER-EXCHANGE", true, false, null);
}
/**
* 延时队列
* @return
*/
@Bean("ORDER-TTL-QUEUE")
public Queue ttlQueue(){
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-dead-letter-exchange", "ORDER-EXCHANGE");
arguments.put("x-dead-letter-routing-key", "order.close");
arguments.put("x-message-ttl", 120000); // 仅仅用于测试,实际根据需求,通常30分钟或者15分钟
return new Queue("ORDER-TTL-QUEUE", true, false, false, arguments);
}
/**
* 延时队列绑定到交换机
* rountingKey:order.create
* @return
*/
@Bean("ORDER-TTL-BINDING")
public Binding ttlBinding(){
return new Binding("ORDER-TTL-QUEUE", Binding.DestinationType.QUEUE, "ORDER-EXCHANGE", "order.create", null);
}
/**
* 死信队列
* @return
*/
@Bean("ORDER-CLOSE-QUEUE")
public Queue queue(){
return new Queue("ORDER-CLOSE-QUEUE", true, false, false, null);
}
/**
* 死信队列绑定到交换机
* routingKey:order.close
* @return
*/
@Bean("ORDER-CLOSE-BINDING")
public Binding closeBinding(){
return new Binding("ORDER-CLOSE-QUEUE", Binding.DestinationType.QUEUE, "ORDER-EXCHANGE", "order.close", null);
}
}
测试死信队列:
@RunWith(SpringRunner.class)
@SpringBootTest
public class GmallOmsApplicationTests {
@Autowired
private AmqpTemplate amqpTemplate;
@Test
public void contextLoads() {
this.amqpTemplate.convertAndSend("ORDER-EXCHANGE", "order.create", "hello world!");
}
}
创建死信监听:
@Component
public class DeadListener {
@RabbitListener(queues = "ORDER-CLOSE-QUEUE")
public void testDead(String msg){
System.out.println(msg);
}
}
样例流程图: