Java 或 SpringBoot 实现延迟执行
项目上需要增加一个竞拍的功能,不需要考虑什么同步、异步,或者并发的问题(那是消息队列的事),只需要在提交报价的时候实现一个 5 分钟机制即可。
设置起始价和加价幅度,延时周期为5分钟。如果在拍卖结束前5分钟出价,拍卖结束时间会在出价时间基础上延长5分钟,直至无人出价,拍卖结束。
这里主要是要实现 5 分钟后执行这样一个延迟执行的需求。一开始考虑的是事件机制(事实证明是我想多了),Laravel 框架有一个 队列事件监听器,其实使用的还是队列的 延迟调度。Springboot 测试项目中测试了 java 事件,确实没有延迟的可能性。
考虑使用延时队列,百度找到了一个 SpringBoot Event 事件如何实现异步延迟执行。通过 event 实现 Delayed 接口,再改写自定义事件发布器,实现 ApplicationRunner 启动监听延时队列 DelayQueue,然后定义一个发布方法,将 delayEvent 添加到 DelayQueue 中,实现逻辑闭环。测试可以实现延迟执行。
但考虑到线上的环境是 Spring Cloud 部署了两个应用做负载均衡,对于 Spring Cloud 不太了解,如果两个应用分别建立这样的延迟队列,是否会出现一些异常情况。比如,竞价者1提交报价到服务器1,竞价者2提交报价到服务器2,然后竞价者2处理时不知道前面有竞价者1。最后证明这是庸人自扰,实际处理会查询数据库(唯一的服务),所以多个应用维护多个延迟队列反而效率更高。
回顾 Laravel 项目中也是部署了多个应用,然后队列的驱动使用了 redis,用于存放队列任务。而每个应用会启动一个或多个 worker 去消费、处理这些任务,目前来看也没法出现什么异常情况。
询问 Java 同事有关延迟队列,他们提到的是 Kafka,但他们印象中没有延时执行的机制。我知道一个消息队列 rabbitMQ,物联网项目中有用到延迟执行,但不清楚具体的实现过程。此时同事还提到了定时器 Timer,类似于 js 中的定时器。schedule 方法启动定时器,有多个重载方法,创建之后可以设置延时执行,以及之后多次执行的间隔。
Timer timer = new Timer();
timer.schedule(new BidTimerTask(jpCar.getId(), bidService), 5 * 60 * 1000);
使用非常简单,因为需要在创建的 Task 中执行逻辑操作,之前直接在 Task 中自动注入 service 报错,说找不到,所以通过构造函数参数传递到 Task 中。测试 OK!
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。