quartz存在一個問題,當正在進行的任務已經達到了設置的個數,後續觸發的任務沒有線程可用,會導致系統宕機;
Quartz通過StdSchedulerFactory工廠創建調度器,initialize方法通過解析quartz.properties配置信息進行加載,默認線程個數爲10,可以通過覆蓋信息定義線程個數,通過Apollo信息進行線程個數配置
Scheduler scheduler = (new StdSchedulerFactory()).getScheduler(); //獲取Apollo配置信息 Config config = ConfigService.getConfig("application"); String threadCount = config.getProperty("org.quartz.threadPool.threadCount", ""); //對quartz.properties配置信息進行覆蓋 Properties props = new Properties(); InputStream in = TaskScheduleServiceImpl.class.getClassLoader().getResourceAsStream("quartz.properties"); props.load(in); String threadCountProperties = props.get("org.quartz.threadPool.threadCount").toString(); if ("".equals(threadCount)) { if (!threadCount.equals(threadCountProperties)) { props.setProperty("org.quartz.threadPool.threadCount", threadCount); //重新創建調度器 StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory(); stdSchedulerFactory.initialize(props); //關閉原先調度器 scheduler.shutdown(); scheduler = stdSchedulerFactory.getScheduler(); in.close(); } }
上面代碼是在系統初始化中執行的,若想實現系統運行中進行動態擴容/縮容調度器線程池大小,可以加一個flag標誌進行判斷,通過獲取當前系統正在執行的任務個數和系統定義的總數進行對比,當達到一定個數或一定比例時,再次調用上面代碼進行動態擴容/縮容。
上面代碼存在的問題是當關閉調度器時,會殺死原先存在正在進行的任務,這是不友好的操作,可以添加一箇中間件(Redis),將每次觸發執行的任務加入Redis中,執行完從Redis刪除,所以每次系統重啓都從Redis讀取數據,對任務重新執行,執行完再將其從Redis刪除,同時爲了保證任務的唯一性(集羣環境下不管是定時還是立即執行),需要對任務進行加鎖操作,由於只需要保證同一時刻只能有一個任務在運行,保證高可用而不追求強一致性,所以選擇的是Redis分佈式鎖Redisson,根據先到先得策略使用了Redisson的公平鎖。