写于:2019-05-07 23:00:30

推荐书籍:《RabbitMQ实战指南 》朱忠华 。欢迎购买正版书籍。 想要 电子版参考的小伙伴,可以关注公众号 【WTF名字好难取】回复,【推荐书籍】,获取下载地址。

本文参考:《RabbitMQ实战指南》4.2 , 4.3 ,4.4 章节,进行实现并落地。

# RabbitMq 实现延迟队列

通过 RabbitMQ 延迟队列,我们需要关注的2个知识点(这里不进行知识普及,可以通过推荐书籍,官方网址等其他方式获取基础知识

  • 消息的过期时间(TTL)
  • 死信队列

消息延迟队列实现的原理如下图:

延迟队列原理图

原理解析: 1、生产者发送消息的队列中,且针对该队列没有消费者 2、由于队列没有消费者,消息在过期之后会进入到死信队列中 3、监听死信队列,并对死信队列中的消息进行消费

通过队列中的消息延迟转发实现延迟队列。

# RabbitMq 延迟队列本身的特性,导致的问题。

通过查看相关RabbitMq 介绍内容,进行相关测试。

测试代码如下

// 发送消息
@RequestMapping("/send-msg")
public String placeOrder(@RequestParam(value = "expiration",defaultValue = "60000")String expiration){
	// msg 属性,如 TTL
	MessageProperties msgProperties = new MessageProperties();
    msgProperties.setExpiration(expiration);
	// msg 内容
    Message msg = new Message("send-msg".getBytes(),msgProperties);
    CorrelationData correlationData = new CorrelationData(orderNo);
	// 发送消息
	rabbitTemplate.send(RabbitMqConfigEnum.ORDER_DELAY.Exchange(),
                    RabbitMqConfigEnum.ORDER_DELAY.RoutingKey(),msg,correlationData);
        return "success";
}
//消费死信队列
@RabbitListener(queues = "ORDER_DELAY_DEAD_LETTER_QUEUE")
public void receiveMessage(Message message){
	log.info("【消费:{}】 ",new String(message.getBody()));
}

测试步骤

1、先发送一条 TTL = 60000 ms(1分钟)的消息 msg-1 到队列中。 2、在发送一条 TTL = 1ms 的消息 msg-2 到队列中

测试结果 msg-1 消费完之后,消费 msg-2

测试结论

测试结论图

RabbitMq 队列是有顺序的,按照先进先出的原则。消息是否从 Queue 进入 Dead Letter Queue 需要从头部第一条信息开始判断,当第一条消息进入 Dead Letter Queue 中后,第二条消息变成第一条,然后依次循环操作。 也就是说,即使 第二条消息 msg-2 比 第一条 msg-1 先过期,也需要等到 msg-1 进入 Dead Letter Queue 中时 msg-2 才会进入到 Dead Letter Queue 中。

从以上结论得出,使用 RabbitMq 实现的延迟队列,无法按照随机过期时间,精准消费延时的消息。针对随机分布式的过期时间,RabbitMq 延迟队列无能为力。如果是定制的多个过期时间,可以针对不同的时间分别定制不同延时队列组合。

延时队列组合

# 延迟队列---实现未支付订单状态更改

背景

在系统中,存在下单操作。而下单的支付操作对接的一般也是第三方平台的支付系统。 用户下单生成订单,订单状态为待支付,等待第三方支付回调更新状态。 在规定时间内:如 15 分钟内,如果订单仍然为 待支付状态,修改订单状态为 取消。

常规做法

通过 定时任务 进行处理。

定时任务 处理存在的问题: 1、订单状态的时效性无法得到保障 2、集中处理订单数据,数据量大时,可能导致瞬时的msql 写操作增加。

使用 RabbitMq 进行状态延迟变更可行性分析

  • 1、如何保证消息的可靠发送?

    通过 confirm 确认机制,保证消息至少发送一次

  • 2、如何保证消息一定会被消费,且只消费一次?

    通过持久化队列,持久化消息的方式,只要 消息发送到 RabbitMq 中,只要未被确认消费,消息就会一直存在。 由于消息的可靠发送,导致的消息可能重复发送的问题,通过消费接口的幂等操作。保证消息不会重复消费。 如:UPDATE 订单表 SET 订单状态 = 订单取消 where 订单状态 = 待支付 AND 订单号 = orderNo

  • 3、使用 RabbitMq 为什么优于定时任务?

    a、RabbitMq 能够实现 订单状态近实时的状态的变更。

    b、根据用户下单的时间不同,分散操作订单状态的时间点,防止非用户行为导致的瞬时数据库操作压力

  • 4、队列长度达到阈值,是否导致未过期消息提前进入死信队列?

    首先,RabbitMq 队列在未指定队列长度时,默认是没有上限的。其上限与机器内存,磁盘空间等有关。

由于代码简单这里不进行代码黏贴。 RabbitMq_延迟队列_Demo结构

精彩内容推送,请关注公众号!
最近更新时间: 3/26/2020, 12:09:24 PM