接入微信支付的時候,看到微信支付的回調是按照某種頻率去回調的,
像15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h
這樣,其中有一次成功就不會再回調。
因而在想怎麼用Java
作這個事情。
有定時任務這類功能的框架像Spring
和Quartz
貌似都沒有直接提供以上的功能。
也是出於想練手的目的,決定本身寫一寫。java
// 具體的業務 BaseJob task = new BaseJob() { // 任務執行的次數(模擬真實業務上的退出) int runTime = 1; @Override public void run() { // 業務邏輯 System.out.println("hello world"); // 這裏模擬了微信回調成功,任務完成 if (runTime++ > 3) { this.setExit(true); } } };
/** * 測試按照指定時間隔執行某個任務 * @throws IOException */ @Test public void test1() throws IOException { // 新建一個產生指定時間的延遲時間生成器,內部就是個隊列 DesignatDTGenerator designatDTGenerator = new DesignatDTGenerator(); // 設置時間間隔 designatDTGenerator.addDelayTime(1_000) // 1秒後執行 .addDelayTime(4_000) // 距離上次執行4秒後執行 .addDelayTime(15_000) // 距離上次執行15秒後執行 .addDelayTime(180_000) // 距離上次執行3分鐘後執行 .addDelayTime(180_000) // 距離上次執行3分鐘後執行 .addDelayTime(360_000) // 距離上次執行6分鐘後執行 .addDelayTime(3_600_000); // 距離上次執行1小時後執行 // 構造一個提交的任務,傳入具體的業務對象task,傳入延遲時間生成器designatDTGenerator DelayTimeJob delayTimeJob = new DelayTimeJob(task, designatDTGenerator); // 新建一個執行器,執行器能夠重複使用,每次提交新的任務便可 JobActuator actuator = new JobActuator(); // 提交任務,開始執行任務 actuator.addJob(delayTimeJob); // 阻塞主線程,方便查看運行結果 System.in.read(); }
/** * 測試按照固定時間間隔執行某個任務 * 只是延遲時間生成器不一樣而已,能夠達到不一樣的調用效果 * @throws IOException */ @Test public void test2() throws IOException { // 新建一個執行器 JobActuator actuator = new JobActuator(); // 新建一個產生固定時間的延遲時間生成器,每3s執行一次 FixedRateDTGenerator fixedRateDTGenerator = new FixedRateDTGenerator(3000); // 新建一個任務 DelayTimeJob delayTimeJob = new DelayTimeJob(task, fixedRateDTGenerator); // 提交任務,開始執行任務 actuator.addJob(delayTimeJob); // 阻塞主線程,方便查看運行結果 System.in.read(); }
項目地址git
JobActuator
任務執行器,自己繼承了Thread
,職責是在run
方法中不斷從延遲任務隊列DelayQueue
中獲取延遲到期的任務,
再交由線程池ExecutorService
執行。延遲效果的都是依靠DelayQueue
實現。
public class JobActuator extends Thread { /** 線程池 */ ExecutorService es = Executors.newFixedThreadPool(2); /** 任務隊列 */ DelayQueue<DelayTimeJob> jobs = new DelayQueue<>(); /** 構造方法,實例化時啓動線程 */ public JobActuator() { this.start(); } public void addJob(DelayTimeJob job) { // 設置任務隊列,用於任務從新入隊 job.setJobs(jobs); // 任務入隊 jobs.offer(job); } @Override public void run() { while (true) { try { // 從延遲隊列中獲取任務 DelayTimeJob job = jobs.take(); // 利用線程池執行任務 es.submit(job); } catch (InterruptedException e) { e.printStackTrace(); } } } }
DelayTimeJob
實現了Delayed
接口,執行實際的業務並決定任務是否從新進入延遲隊列。
public class DelayTimeJob implements Runnable, Delayed { /** 執行器的任務隊列,用於任務從新入隊 */ @Setter private DelayQueue<DelayTimeJob> jobs; /** 延遲時間生成器 */ IDelayTimeGenerator delayTimeGenerator; /** 具體要執行的任務 */ private BaseJob realJob; private long time = 0L; public DelayTimeJob(BaseJob baseJob, IDelayTimeGenerator delayTimeGenerator) { this.realJob = baseJob; this.delayTimeGenerator = delayTimeGenerator; Integer delayTime = delayTimeGenerator.getDelayTime(); if (delayTime == null) { return ; } this.time = delayTime + System.currentTimeMillis(); } @Override public void run() { // 執行業務 realJob.run(); // 任務再也不須要執行,主動退出 if (realJob.isExit) { return ; } // 獲取延遲 Integer delayTime = delayTimeGenerator.getDelayTime(); // 無延遲時間,則任務再也不執行 if (delayTime == null) { return ; } // 從新入隊 time += delayTime; jobs.offer(this); return ; } @Override public long getDelay(TimeUnit unit) { return unit.convert(this.time - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed o) { DelayTimeJob other = (DelayTimeJob) o; long diff = time - other.time; if (diff > 0) { return 1; } if (diff < 0) { return -1; } return 0; } }
BaseJob
用戶繼承此抽象類,在run
方法中編寫業務代碼,經過控制isExit
變量控制任務是否執行。
public abstract class BaseJob implements Runnable { /** 用於控制任務是否退出 */ @Setter boolean isExit = false; }
IDelayTimeGenerator
延遲時間生成器接口,返回一個延遲時間。能夠實現不一樣的策略,達到不一樣的延遲效果。
如DesignatDTGenerator
是定義每一次執行的時間間隔,FixedRateDTGenerator
是按照某一個固定頻率執行。
public interface IDelayTimeGenerator { /** 返回延遲的時間,單位:毫秒 */ Integer getDelayTime(); }
/** * 指定時間的時間生成器 * @author cck */ public class DesignatDTGenerator implements IDelayTimeGenerator { private final Deque<Integer> delayTimeQueue = new ArrayDeque<>(); /** * 添加延遲時間 * @param delayTime */ public DesignatDTGenerator addDelayTime(Integer delayTime) { delayTimeQueue.offer(delayTime); return this; } @Override public Integer getDelayTime() { return delayTimeQueue.poll(); } }
/** * 固定間隔的時間生成器 * @author cck */ public class FixedRateDTGenerator implements IDelayTimeGenerator { private Integer delayTime; public FixedRateDTGenerator(Integer delayTime) { this.delayTime = delayTime; } @Override public Integer getDelayTime() { return delayTime; } }
DelayQueue
和Delayed
DelayQueue
是Java
提供的延遲隊列,該隊列只容許實現了Delayed
接口的對象入隊。
調用隊列的take
方法時,隊列會阻塞,直到有延遲到期的元素纔會返回。github
這個方式是能夠實現一開始想要的按照15s/15s/30s/3m/10m/..
指定的間隔執行任務的效果的。
定製延遲的效果只須要給出不一樣的IDelayTimeGenerator
接口實現便可。redis
在和spring
一塊兒使用時,任務執行器JobActuator
應該是單例的,
不過提交任務的整個操做相比於spring
的一個註解,仍是顯得麻煩囧,使用時再封裝一層會更好。spring
如今的實現方式是和Java
的延遲隊列綁定了的,可是延遲隊列有多種實現方式,
例如redis
,rabbitMQ
等,若是可以作出更高級的抽象,合入不一樣的延遲隊列那會更好。
此外這種實現方式性能方面也有待驗證。微信