一、定义延迟任务类
package com.activity.domain;import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;/*** 延迟任务类*/
public class DelayedCancellation implements Delayed {private String order;private final long delayTime; // 延迟时间public DelayedCancellation(String order, long delayTime) {this.order = order;this.delayTime = System.currentTimeMillis() + delayTime;}public String getOrder() {return order;}@Overridepublic long getDelay(TimeUnit unit) {return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);}@Overridepublic int compareTo(Delayed o) {return Long.compare(this.delayTime, ((DelayedCancellation) o).delayTime);}
}
二、执行任务
package com.activity.utils;import java.util.concurrent.DelayQueue;import com.zaiyun.activity.domain.DelayedCancellation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class CancellationManager {private static final Logger wechatLogger = LoggerFactory.getLogger("extend-wechat");private final DelayQueue<DelayedCancellation> delayQueue = new DelayQueue<>();public void scheduleOrderCancellation(String order, long delayTime) {DelayedCancellation delayedOrderCancellation = new DelayedCancellation(order, delayTime);delayQueue.put(delayedOrderCancellation);}public void startOrderCancellationScheduler() {new Thread(() -> {while (true) {try {DelayedCancellation delayedOrderCancellation = delayQueue.take();processOrderCancellation(delayedOrderCancellation.getOrder());} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}).start();}/*** 执行取消操作* @param order*/private void processOrderCancellation(String order) {wechatLogger.info("执行取消任务-订单编号:" + order);}
}
三、触发使用
/*** 30分钟未支付将取消参与*/public static void cancelParticipation(String order) {CancellationManager cancellationManager = new CancellationManager();cancellationManager.startOrderCancellationScheduler();cancellationManager.scheduleOrderCancellation(order, TimeUnit.MINUTES.toMillis(30));wechatLogger.info("触发延时队列-订单编号:" + order);}
四、执行日志
五、服务重启问题
在调试的过程中发现一个严重的问题 (重启服务后任务丢失了)
因为延迟队列没有做持久化,那么服务重启之后,原来在队列的任务就丢失啦。所以,服务重启的时候要去扫描检测订单。
ApplicationRunner执行时机为容器启动完成的时候,实现run方法即可。或使用InitializingBean接口。
package com.activity.domain;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;/*** 初始化执行任务*/
@Component
public class InitializeTask implements ApplicationRunner {private static final Logger wechatLogger = LoggerFactory.getLogger("extend-wechat");@Overridepublic void run(ApplicationArguments args) {wechatLogger.info("初始化执行任务:");}
}
本文参考 使用延迟队列处理超时订单