一、定義延遲任務類
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("初始化執行任務:");}
}
本文參考 使用延遲隊列處理超時訂單