rabbitmq

咱们用大白话来好好理解一下这段代码的流程。这段代码的核心功能是发送和处理延迟消息 ,具体来说,它被用来实现订单的自动取消功能。

想象一下这样的场景:用户创建了一个订单,但是过了一段时间(比如 15 秒)没有付款,我们希望自动取消这个订单。这段代码就是用来实现这个"延迟取消"的功能的。

我们先来看发送延迟消息的部分,也就是 sendDelayMessage 这个方法:

java 复制代码
/**
 * 发送延迟消息
 */
private void sendDelayMessage(Long orderId) {
    try {
        //  创建一个队列
        RBlockingDeque<Object> blockingDeque = redissonClient
            .getBlockingDeque("queue_cancel");
        //  将队列放入延迟队列中
        RDelayedQueue<Object> delayedQueue = redissonClient
            .getDelayedQueue(blockingDeque);
        //  发送的内容
        delayedQueue.offer(orderId.toString(),
                           15, TimeUnit.SECONDS);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  1. private void sendDelayMessage(Long orderId) : 这个方法就像一个"发号员",它接收一个 orderId(订单的唯一编号),告诉系统我们需要对这个订单设置一个延迟任务。

  2. RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque("queue_cancel"); : 这一行代码的意思是,我们从 Redisson 客户端获取了一个叫做 "queue_cancel"阻塞队列。你可以把这个队列想象成一个停车场,只不过这个停车场是先进先出的,先来的车停在前面。这里的 "阻塞" 的意思是,如果停车场是空的,想要从里面取车的车辆(后面的处理逻辑)会一直等待,直到有车开进来。

  3. RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque); : 这一行更关键。我们不是直接把订单 ID 放到普通的队列里,而是通过 redissonClient.getDelayedQueue(blockingDeque) 创建了一个延迟队列 ,并且这个延迟队列是和我们刚刚创建的阻塞队列 "queue_cancel" 关联起来的。你可以把延迟队列想象成一个特殊的通道,里面的消息不会立刻进入停车场,而是要等一段时间才能放行。

  4. delayedQueue.offer(orderId.toString(), 15, TimeUnit.SECONDS);: 这行代码就是真正发送延迟消息的操作。

    • orderId.toString(): 我们把订单的 ID(转换成字符串)作为消息放进延迟队列。
    • 15: 这是延迟的时间长度,这里是 15。
    • TimeUnit.SECONDS: 这是时间单位,这里是秒。 所以,这行代码的意思是:"把这个订单 ID 放进延迟队列,让它在 15 秒 之后才能被放进 queue_cancel 这个阻塞队列里。"
  5. catch (Exception e) { e.printStackTrace(); }: 这是一个标准的异常处理,如果发送过程中出现任何错误,就打印错误信息。

接下来,我们再来看处理延迟消息的部分,也就是 RedisDelayHandle 这个类和它的 listener 方法:

java 复制代码
@Slf4j
@Component
public class RedisDelayHandle {

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private OrderInfoService orderInfoService;

    @PostConstruct
    public void listener() {
        new Thread(()->{
            while (true){
                // 获取到阻塞队列
                RBlockingDeque<String> blockingDeque = redissonClient
                        .getBlockingDeque("queue_cancel");
                try {
                    // 从阻塞队列中获取到订单Id
                    String orderId = blockingDeque.take();
                    // 订单Id 不为空的时候,调用取消订单方法
                    if(!StringUtils.isEmpty(orderId)) {
                        log.info("接收延时队列成功,订单id:{}", orderId);
                        orderInfoService.orderCancel(Long.parseLong(orderId));
                    }
                } catch (InterruptedException e) {
                    log.error("接收延时队列失败");
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
  1. @Slf4j@Component : 这两个是 Spring 的注解。@Slf4j 会自动帮我们创建一个日志对象 log,用来记录程序的运行信息。@Component 表明这是一个 Spring 管理的组件。

  2. @Autowired private RedissonClient redissonClient; : 同样是 Spring 的注解,它会自动注入一个 RedissonClient 的实例,这样我们就可以使用 Redisson 提供的功能来操作 Redis。

  3. @Autowired private OrderInfoService orderInfoService; : 这个注解注入了一个 OrderInfoService 的实例。这个 Service 应该是用来处理订单相关的业务逻辑,比如取消订单。

  4. @PostConstruct public void listener() { ... } : @PostConstruct 注解表示这个 listener 方法会在 RedisDelayHandle 这个 Bean 被 Spring 创建之后自动执行。这个方法的主要作用是启动一个新的线程来持续监听延迟队列中的消息。

  5. new Thread(()->{ while (true){ ... } }).start(); : 这里创建并启动了一个新的线程。while (true) 表示这个线程会一直运行下去,不断地从队列中获取消息。

  6. RBlockingDeque<String> blockingDeque = redissonClient.getBlockingDeque("queue_cancel"); : 在这个线程里,我们又一次获取了名为 "queue_cancel"阻塞队列。注意,这里的队列名称和发送延迟消息时用的是同一个。

  7. String orderId = blockingDeque.take(); : 这里的 blockingDeque.take() 是一个阻塞方法 。它会尝试从 "queue_cancel" 这个队列中取出一个元素。如果队列是空的,这个线程就会一直等待,直到有新的元素被放进来(也就是延迟时间到了,延迟队列把消息放进来了)。一旦取到了一个元素,这个元素就是我们之前放进去的订单 ID(以字符串的形式)。

  8. if(!StringUtils.isEmpty(orderId)) { ... }: 拿到订单 ID 之后,这里会判断它是否为空。

  9. log.info("接收延时队列成功,订单id:{}", orderId);: 如果订单 ID 不为空,就记录一条日志,表明成功从延迟队列中接收到了订单 ID。

  10. orderInfoService.orderCancel(Long.parseLong(orderId)); : 这一行是关键的业务逻辑!我们通过之前注入的 OrderInfoService 调用了 orderCancel 方法,并将接收到的订单 ID(转换成 Long 类型)传递给它。这个方法应该会执行实际的取消订单的逻辑,比如更新数据库中的订单状态等等。

  11. catch (InterruptedException e) { ... }: 这是一个异常处理,用来捕获线程在等待从队列中取数据时被中断的异常。如果发生中断,就记录错误日志并打印堆栈信息。

总结一下整个流程:

  1. 当需要设置一个延迟任务(比如 15 秒后取消订单)时,sendDelayMessage 方法会被调用,它会将订单 ID 放入一个与阻塞队列关联的延迟队列中,并设置延迟时间。

  2. 在延迟时间到达之前,queue_cancel 这个阻塞队列是空的。

  3. RedisDelayHandle 中运行的监听线程会一直尝试从 queue_cancel 这个阻塞队列中 take() 数据,由于队列是空的,它会一直阻塞等待。

  4. 当 15 秒的延迟时间到了,Redisson 内部的机制会将之前放入延迟队列的订单 ID 取出来,并把它放回 queue_cancel 这个阻塞队列中。

  5. 这时,之前一直阻塞等待的监听线程被唤醒,它成功地从 queue_cancel 队列中 take() 到了订单 ID。

  6. 监听线程拿到订单 ID 后,会调用 orderInfoService.orderCancel() 方法,执行真正的取消订单操作。

  7. 这个监听线程会一直运行在一个无限循环中,不断地从 queue_cancel 队列中获取到期的延迟消息并进行处理。

相关推荐
秋野酱7 分钟前
基于javaweb的SpringBoot自习室预约系统设计与实现(源码+文档+部署讲解)
java·spring boot·后端
面试官E先生27 分钟前
【极兔快递Java社招】一面复盘|数据库+线程池+AQS+中间件面面俱到
java·面试
琢磨先生David32 分钟前
构建优雅对象的艺术:Java 建造者模式的架构解析与工程实践
java·设计模式·建造者模式
小雅痞1 小时前
[Java][Leetcode simple]26. 删除有序数组中的重复项
java·leetcode
青云交1 小时前
Java 大视界 -- 基于 Java 的大数据分布式存储在工业互联网海量设备数据长期存储中的应用优化(248)
java·大数据·工业互联网·分布式存储·冷热数据管理·hbase 优化·kudu 应用
纸包鱼最好吃1 小时前
java基础-package关键字、MVC、import关键字
java·开发语言·mvc
唐山柳林1 小时前
城市生命线综合管控系统解决方案-守护城市生命线安全
java·安全·servlet
PgSheep1 小时前
Spring Cloud Gateway 聚合 Swagger 文档:一站式API管理解决方案
java·开发语言
蒂法就是我2 小时前
详细说说Spring的IOC机制
java·后端·spring
程序员拂雨2 小时前
Java知识框架
java·开发语言