一.延时队列的概念
当前消息不想让消费者立马拿到,等到特定时间再进行消费
二.使用场景
订单30分钟后过期,
订阅商品信息,特定时间发送短信,
等…
三.实现思路
设置TTL+死信队列,到达设置的过期时间,消息会进入到死信队列,消费者监听死信队列
四.缺陷
延迟消费极限时长为约49天
设置过期时间有两种
设置队列的TTL: 不可单独设置每条消息具体的过期时间
设置消息的TTL: 队列形式,先进先出,可能后面进的消息早已过期
都设置情况下,取最短
五.具体实现
1.POM
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.yml配置文件
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
3.properties配置文件
#订单业务队列的名称,交换机名称、路由ke
delay.bussiness.queue: order
delay.bussiness.excahnge: order_exchange
delay.bussiness.route: order_776
#死信队列的名称,交换机名称、路由ke
delay.dead.queue: dead_order
delay.dead.excahnge: dead_order_exchange
delay.dead.route: dead_order_666
4.参数类,方便后面使用
@Configuration
@Data
public class OrderDelayConfig {
@Value("${delay.bussiness.queue}")
private String orderDelayQueueName;
@Value("${delay.bussiness.excahnge}")
private String orderDelayExchangeName;
@Value("${delay.bussiness.route}")
private String orderDelayRouteKey;
@Value("${delay.dead.queue}")
private String orderDeadDelayQueueName;
@Value("${delay.dead.excahnge}")
private String orderDeadDelayExchangeName;
@Value("${delay.dead.route}")
private String orderDeadDelayRouteKey;
}
6.绑定队列交换机
@Configuration
public class RabbitConfig {
//rabbitMq内置死信队列信息
private final String DLEXCHANGE = "x-dead-letter-exchange";
private final String DLROUTEKEY = "x-dead-letter-routing-key";
private final String TTL = "x-message-ttl";
@Autowired
private OrderDelayConfig orderDelayConfig;
//创建死信交换机
@Bean("orderDeadExchange")
public DirectExchange orderDeadExchange() {
return new DirectExchange(orderDelayConfig.getOrderDeadDelayExchangeName());
}
//创建死信队列
@Bean("orderDeadQueue")
public Queue orderDeadQueue() {
return new Queue(orderDelayConfig.getOrderDeadDelayQueueName());
}
//创建业务交换机
@Bean("orderExchange")
public DirectExchange orderExchange() {
return new DirectExchange(orderDelayConfig.getOrderDelayExchangeName());
}
//创建业务队列
@Bean("orderQueue")
public Queue orderQueue() {
Map<String, Object> params = new HashMap<>();
//设置队列的过期时间,这是直接设置队列过期的
// params.put(TTL, 5000);
//声明当前队列绑定的死信交换机
params.put(DLEXCHANGE, orderDelayConfig.getOrderDeadDelayExchangeName());
//声明当前队列的死信路由键
params.put(DLROUTEKEY, orderDelayConfig.getOrderDeadDelayRouteKey());
return QueueBuilder.durable(orderDelayConfig.getOrderDelayQueueName()).withArguments(params).build();
}
//业务队列与业务交换机绑定
@Bean
public Binding orderBinding(@Qualifier("orderQueue") Queue queue,
@Qualifier("orderExchange") DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(orderDelayConfig.getOrderDelayRouteKey());
}
//死信队列与死信交换机绑定
@Bean
public Binding orderDeadBinding(@Qualifier("orderDeadQueue") Queue deadQueue,
@Qualifier("orderDeadExchange") DirectExchange exchange) {
return BindingBuilder.bind(deadQueue).to(exchange).with(orderDelayConfig.getOrderDeadDelayRouteKey());
}
}
7.主要消息消费和发送
@Component
@EnableScheduling
@Slf4j
public class OrderDelayQueue {
private RabbitTemplate rabbitTemplate;
private OrderDelayConfig orderDelayConfig;
private final static String deadQueueName = "dead_order";
public OrderDelayQueue(RabbitTemplate rabbitTemplate, OrderDelayConfig orderDelayConfig) {
this.rabbitTemplate = rabbitTemplate;
this.orderDelayConfig = orderDelayConfig;
}
/**
* 发送消息
* @param msg 消息体
* @param expDate 过期时间
*/
public void rabbitSendMsg(String msg,Date expDate ) {
//给消息设置过期时间
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
// 单位毫秒,如果有更早的过期时间,自己处理成0就行
message.getMessageProperties().setExpiration(String.valueOf(expDate.getTime()-new Date().getTime()));
return message;
}
};
rabbitTemplate.convertAndSend(orderDelayConfig.getOrderDelayExchangeName(),
orderDelayConfig.getOrderDelayRouteKey(), msg,messagePostProcessor);
}
//消费死信队列的消息
@RabbitListener(queues = deadQueueName)
public void infoConsumption(String data) throws Exception {
//此处编写执行订单超时状态的逻辑代码
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.info(data + ",已过期,当前时间:" + formatter.format(new Date()));
}
}
8.测试demo
@RestController()
@RequestMapping(value = "/test")
@Slf4j
public class Test {
@Autowired
private OrderDelayQueue orderDelayQueue;
@GetMapping(value = "/mq")
public void test(String order,Long dateLong){
Date date = new Date(new Date().getTime() + dateLong);
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.info("传进来的过期时间为:"+formatter.format(date));
orderDelayQueue.rabbitSendMsg(order,date);
}
}
9.结果
请求链接
http://localhost:8080/test/mq?order=订单22&dateLong=10000
结果