以前公司平臺中集成了定時任務功能,但平臺內部實現比較簡單,使用方式有些受限,比如說無法跟蹤定時任務執行狀態,無法自動解決集群狀態下的任務爭搶問題,因此考慮升級一下任務實現方式,搜集一番后,Quartz和Xxl-Job都能滿足現在的需求;
以下就對兩種定時任務框架進行簡單說明。
Quartz
傳送門
github地址:
https://github.com/quartz-scheduler/quartz
Maven坐標
<!-- Quartz Core -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
可以查看jar包的依賴情況如下:
如果不是maven項目,單獨下載jar包使用的情況,不要漏掉jar包;可通過官網下載的方式獲取到所有的jar包;
官網不知道怎么回事兒,超級慢;
我一般的做法是,本地建立一個單獨的maven項目,加入相關maven依賴,然后通過下面的maven命令獲取到所有pom.xml文件中的jar包;然后再復制到自己需要放的地方;
mvn dependency:copy-dependencies -DoutputDirectory=lib
配置文件
最終編譯后的位置:WEB-INF/classes/quartz.properties
下面是一個最基本的配置項內容:
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
其中
此配置創建的調度器有以下特點:
- org.quartz.scheduler.instanceName -這個調度程序的名稱將是“MyScheduler”。
- org.quartz.threadPool.threadCount- 線程池中有3個線程,這意味著最多可以同時運行3個任務。
- org.quartz.jobStore.class - Quartz的所有數據,比如任務和觸發器的細節,都保存在內存中(而不是數據庫中)。即使你有一個數據庫,并且想要在Quartz上使用它,我建議你先讓Quartz和RamJobStore一起使用,然后再用數據庫打開一個全新的維度。
任務信息
任務信息處理類實現了org.quartz.Job 接口;如下
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext)
throws JobExecutionException {
System.out.println("hello @ "
+ LocalDateTime
.now()
.format(DateTimeFormatter
.ofPattern("yyyyMMddHHmmss")));
}
}
任務調度
一旦使用
StdSchedulerFactory.getDefaultScheduler()獲得一個調度器,您的應用程序將不會終止,直到您調用schedul. shutdown(),因為將有活動線程。
注意代碼示例中的靜態導入 ;
這些將在下面的代碼示例中發揮作用。
更詳細地配置文件說明在這兒:
https://github.com/quartz-scheduler/quartz/blob/master/docs/configuration.adoc
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.JobBuilder.*;
import static org.quartz.TriggerBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
public class QuartzTest {
public static void main(String[] args) throws InterruptedException {
try {
// Grab the Scheduler instance from the Factory
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// and start it off
scheduler.start();
// define the job and tie it to our HelloJob class
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.build();
// Trigger the job to run now, and then repeat every 40 seconds
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(10)
.repeatForever())
.build();
// Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger);
Thread.sleep(60 * 60 * 1000);
scheduler.shutdown();
} catch (SchedulerException se) {
se.printStackTrace();
}
}
}
集群下的任務調度
數據庫表
表文件在jar包的
org.quartz.impl.jdbcjobstore,可根據數據庫類型選擇不同的數據庫文件;
配置文件
quartz也提供了數據庫方面的任務配置及集群下的任務處理;
#==============================================================
#Configure Main Scheduler Properties
#org.quartz.scheduler.instanceName【相同業務部署保持該配置一致】
#==============================================================
org.quartz.scheduler.instanceName = quartzScheduler
org.quartz.scheduler.instanceId = AUTO
#==============================================================
#Configure JobStore
#org.quartz.jobStore.tablePrefix:表名前綴
#org.quartz.jobStore.dataSource:對應org.quartz.dataSource.xxx下面的配置信息
#==============================================================
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 10000
org.quartz.jobStore.dataSource = myDS
#==============================================================
#Configure DataSource
#可以通過配置詳細數據源的方式,也可以通過配置連接池的方式
#org.quartz.dataSource.qzDS.jndiURL = JAVA:/comp/env/jdbc/mydb
#我是用的weblogic中間件直接配置weblogic連接池的jndi即可,例如我的jndi名稱為jdbc/isp
#因此我的配置如下:
#org.quartz.dataSource.qzDS.jndiURL=jdbc/isp
#
#org.quartz.dataSource.myDS.driver為數據庫驅動名稱,
#根據項目中實際需要修改為自己的數據庫驅動名稱即可
#==============================================================
org.quartz.dataSource.myDS.driver = com.ibm.db2.jcc.DB2Driver
org.quartz.dataSource.myDS.URL =
org.quartz.dataSource.myDS.user =
org.quartz.dataSource.myDS.password =
org.quartz.dataSource.myDS.maxConnections = 30
#==============================================================
#Configure ThreadPool
#==============================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
crontab任務創建
// 構建job信息
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(jobName, jobGroup)
.withDescription(description)
.build();
// 表達式調度構建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
scheduleBuilder.withMisfireHandlingInstructionDoNothing();
// 按新的cronExpression表達式構建一個新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder)
.build();
if (!params.isEmpty()) {
// 放入參數,運行時的方法可以獲取
jobDetail.getJobDataMap().putAll(params);
}
Date date = scheduler.scheduleJob(jobDetail, trigger);
Xxl-job
說明
XXL-JOB是一個分布式任務調度平臺,其核心設計目標是開發迅速、學習簡單、輕量級、易擴展。
傳送門
官網:
https://www.xuxueli.com/xxl-job/
gitee傳送門:
https://gitee.com/xuxueli0323/xxl-job/tree/master
maven坐標
<!-- https://mvnrepository.com/artifact/com.xuxueli/xxl-job-core -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.0</version>
</dependency>
快速入門
git clone https://gitee.com/xuxueli0323/xxl-job.git
獲得到目錄結構
初始化數據庫
/xxl-job/doc/db/tables_xxl_job.sql
編譯源碼
xxl-job-admin:調度中心
xxl-job-core:公共依賴
xxl-job-executor-samples:執行器Sample示例(選擇合適的版本執行器,可直接使用,也可以參考其并將現有項目改造成執行器)
:
xxl-job-executor-sample-springboot:Springboot版本,通過Springboot管理執行器,推薦這種方式;
:
xxl-job-executor-sample-frameless:無框架版本;
配置部署調度中心
調度中心項目:xxl-job-admin
作用:統一管理任務調度平臺上的調度任務,負責觸發調度執行,并且提供任務管理平臺。
調度中心配置文件地址:
/xxl-job/xxl-job-admin/src/main/resources/Application.properties【修改數據庫配置】
/xxl-job/xxl-job-admin/src/main/resources/logback.xml
完成上述修改后,然后運行XxlJobAdminApplication
運行成功后通過瀏覽器打開:
http://localhost:8080/xxl-job-admin/,用戶名及密碼:admin/123456
至此“調度中心”項目已經部署成功。
配置部署執行器項目
執行器項目:
xxl-job-executor-sample-springboot (提供多種版本執行器供選擇,現以 springboot 版本為例,可直接使用,也可以參考其并將現有項目改造成執行器)
作用:負責接收“調度中心”的調度并執行;可以直接部署執行器,也可以將執行器集成到現有業務項目中。
執行器配置
配置文件地址:
/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/
application.properties
修改:xxl.job.executor.logpath為本地路徑
logback.xml
修改日志路徑為本地路徑
新建任務
觸發執行
請點擊任務右側 “執行” 按鈕,可手動觸發一次任務執行(通常情況下,通過配置Cron表達式進行任務調度觸發)。
查看日志
請點擊任務右側 “日志” 按鈕,可前往任務日志界面查看任務日志。
在任務日志界面中,可查看該任務的歷史調度記錄以及每一次調度的任務調度信息、執行參數和執行信息。運行中的任務點擊右側的“執行日志”按鈕,可進入日志控制臺查看實時執行日志。
磁盤上的日志文件路徑在xxl.job.executor.logpath
更多
基礎配置:
- 執行器:任務的綁定的執行器,任務觸發調度時將會自動發現注冊成功的執行器, 實現任務自動發現功能; 另一方面也可以方便的進行任務分組。每個任務必須綁定一個執行器, 可在 "執行器管理" 進行設置;
- 任務描述:任務的描述信息,便于任務管理;
- 負責人:任務的負責人;
- 報警郵件:任務調度失敗時郵件通知的郵箱地址,支持配置多郵箱地址,配置多個郵箱地址時用逗號分隔;
觸發配置:
- 調度類型:
無:該類型不會主動觸發調度;
CRON:該類型將會通過CRON,觸發任務調度;
固定速度:該類型將會以固定速度,觸發任務調度;按照固定的間隔時間,周期性觸發;
固定延遲:該類型將會以固定延遲,觸發任務調度;按照固定的延遲時間,從上次調度結束后開始計算延遲時間,到達延遲時間后觸發下次調度;
- CRON:觸發任務執行的Cron表達式;
- 固定速度:固件速度的時間間隔,單位為秒;
- 固定延遲:固件延遲的時間間隔,單位為秒;
任務配置:
- 運行模式:
BEAN模式:任務以JobHandler方式維護在執行器端;需要結合 "JobHandler" 屬性匹配執行器中任務;
GLUE模式(Java):任務以源碼方式維護在調度中心;該模式的任務實際上是一段繼承自IJobHandler的Java類代碼并 "groovy" 源碼方式維護,它在執行器項目中運行,可使用@Resource/@Autowire注入執行器里中的其他服務;
GLUE模式(Shell):任務以源碼方式維護在調度中心;該模式的任務實際上是一段 "shell" 腳本;
GLUE模式(Python):任務以源碼方式維護在調度中心;該模式的任務實際上是一段 "python" 腳本;
GLUE模式(php):任務以源碼方式維護在調度中心;該模式的任務實際上是一段 "php" 腳本;
GLUE模式(NodeJS):任務以源碼方式維護在調度中心;該模式的任務實際上是一段 "nodejs" 腳本;
GLUE模式(PowerShell):任務以源碼方式維護在調度中心;該模式的任務實際上是一段 "PowerShell" 腳本;
- JobHandler:運行模式為 "BEAN模式" 時生效,對應執行器中新開發的JobHandler類“@JobHandler”注解自定義的value值;
- 執行參數:任務執行所需的參數;
高級配置:
- 路由策略:當執行器集群部署時,提供豐富的路由策略,包括;
FIRST(第一個):固定選擇第一個機器;
LAST(最后一個):固定選擇最后一個機器;
ROUND(輪詢):;
RANDOM(隨機):隨機選擇在線的機器;
CONSISTENT_HASH(一致性HASH):每個任務按照Hash算法固定選擇某一臺機器,且所有任務均勻散列在不同機器上。
LEAST_FREQUENTLY_USED(最不經常使用):使用頻率最低的機器優先被選舉;
LEAST_RECENTLY_USED(最近最久未使用):最久未使用的機器優先被選舉;
FAILOVER(故障轉移):按照順序依次進行心跳檢測,第一個心跳檢測成功的機器選定為目標執行器并發起調度;
BUSYOVER(忙碌轉移):按照順序依次進行空閑檢測,第一個空閑檢測成功的機器選定為目標執行器并發起調度;
SHARDING_BROADCAST(分片廣播):廣播觸發對應集群中所有機器執行一次任務,同時系統自動傳遞分片參數;可根據分片參數開發分片任務;
- 子任務:每個任務都擁有一個唯一的任務ID(任務ID可以從任務列表獲取),當本任務執行結束并且執行成功時,將會觸發子任務ID所對應的任務的一次主動調度。
- 調度過期策略:
- 忽略:調度過期后,忽略過期的任務,從當前時間開始重新計算下次觸發時間;
- 立即執行一次:調度過期后,立即執行一次,并從當前時間開始重新計算下次觸發時間;
- 阻塞處理策略:調度過于密集執行器來不及處理時的處理策略;
單機串行(默認):調度請求進入單機執行器后,調度請求進入FIFO隊列并以串行方式運行;
丟棄后續調度:調度請求進入單機執行器后,發現執行器存在運行的調度任務,本次請求將會被丟棄并標記為失敗;
覆蓋之前調度:調度請求進入單機執行器后,發現執行器存在運行的調度任務,將會終止運行中的調度任務并清空隊列,然后運行本地調度任務;
- 任務超時時間:支持自定義任務超時時間,任務運行超時將會主動中斷任務;
- 失敗重試次數;支持自定義任務失敗重試次數,當任務失敗時將會按照預設的失敗重試次數主動進行重試;
你看,奇怪的知識又增加了!