以前公司平臺(tái)中集成了定時(shí)任務(wù)功能,但平臺(tái)內(nèi)部實(shí)現(xiàn)比較簡(jiǎn)單,使用方式有些受限,比如說(shuō)無(wú)法跟蹤定時(shí)任務(wù)執(zhí)行狀態(tài),無(wú)法自動(dòng)解決集群狀態(tài)下的任務(wù)爭(zhēng)搶問(wèn)題,因此考慮升級(jí)一下任務(wù)實(shí)現(xiàn)方式,搜集一番后,Quartz和Xxl-Job都能滿足現(xiàn)在的需求;
以下就對(duì)兩種定時(shí)任務(wù)框架進(jìn)行簡(jiǎn)單說(shuō)明。
Quartz
傳送門(mén)
github地址:
https://github.com/quartz-scheduler/quartz
Maven坐標(biāo)
<!-- Quartz Core -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
可以查看jar包的依賴(lài)情況如下:
如果不是maven項(xiàng)目,單獨(dú)下載jar包使用的情況,不要漏掉jar包;可通過(guò)官網(wǎng)下載的方式獲取到所有的jar包;
官網(wǎng)不知道怎么回事兒,超級(jí)慢;
我一般的做法是,本地建立一個(gè)單獨(dú)的maven項(xiàng)目,加入相關(guān)maven依賴(lài),然后通過(guò)下面的maven命令獲取到所有pom.xml文件中的jar包;然后再?gòu)?fù)制到自己需要放的地方;
mvn dependency:copy-dependencies -DoutputDirectory=lib
配置文件
最終編譯后的位置:WEB-INF/classes/quartz.properties
下面是一個(gè)最基本的配置項(xiàng)內(nèi)容:
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
其中
此配置創(chuàng)建的調(diào)度器有以下特點(diǎn):
- org.quartz.scheduler.instanceName -這個(gè)調(diào)度程序的名稱(chēng)將是“MyScheduler”。
- org.quartz.threadPool.threadCount- 線程池中有3個(gè)線程,這意味著最多可以同時(shí)運(yùn)行3個(gè)任務(wù)。
- org.quartz.jobStore.class - Quartz的所有數(shù)據(jù),比如任務(wù)和觸發(fā)器的細(xì)節(jié),都保存在內(nèi)存中(而不是數(shù)據(jù)庫(kù)中)。即使你有一個(gè)數(shù)據(jù)庫(kù),并且想要在Quartz上使用它,我建議你先讓Quartz和RamJobStore一起使用,然后再用數(shù)據(jù)庫(kù)打開(kāi)一個(gè)全新的維度。
任務(wù)信息
任務(wù)信息處理類(lèi)實(shí)現(xiàn)了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")));
}
}
任務(wù)調(diào)度
一旦使用
StdSchedulerFactory.getDefaultScheduler()獲得一個(gè)調(diào)度器,您的應(yīng)用程序?qū)⒉粫?huì)終止,直到您調(diào)用schedul. shutdown(),因?yàn)閷⒂谢顒?dòng)線程。
注意代碼示例中的靜態(tài)導(dǎo)入 ;
這些將在下面的代碼示例中發(fā)揮作用。
更詳細(xì)地配置文件說(shuō)明在這兒:
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();
}
}
}
集群下的任務(wù)調(diào)度
數(shù)據(jù)庫(kù)表
表文件在jar包的
org.quartz.impl.jdbcjobstore,可根據(jù)數(shù)據(jù)庫(kù)類(lèi)型選擇不同的數(shù)據(jù)庫(kù)文件;
配置文件
quartz也提供了數(shù)據(jù)庫(kù)方面的任務(wù)配置及集群下的任務(wù)處理;
#==============================================================
#Configure Main Scheduler Properties
#org.quartz.scheduler.instanceName【相同業(yè)務(wù)部署保持該配置一致】
#==============================================================
org.quartz.scheduler.instanceName = quartzScheduler
org.quartz.scheduler.instanceId = AUTO
#==============================================================
#Configure JobStore
#org.quartz.jobStore.tablePrefix:表名前綴
#org.quartz.jobStore.dataSource:對(duì)應(yīng)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
#可以通過(guò)配置詳細(xì)數(shù)據(jù)源的方式,也可以通過(guò)配置連接池的方式
#org.quartz.dataSource.qzDS.jndiURL = JAVA:/comp/env/jdbc/mydb
#我是用的weblogic中間件直接配置weblogic連接池的jndi即可,例如我的jndi名稱(chēng)為jdbc/isp
#因此我的配置如下:
#org.quartz.dataSource.qzDS.jndiURL=jdbc/isp
#
#org.quartz.dataSource.myDS.driver為數(shù)據(jù)庫(kù)驅(qū)動(dòng)名稱(chēng),
#根據(jù)項(xiàng)目中實(shí)際需要修改為自己的數(shù)據(jù)庫(kù)驅(qū)動(dòng)名稱(chēng)即可
#==============================================================
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任務(wù)創(chuàng)建
// 構(gòu)建job信息
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(jobName, jobGroup)
.withDescription(description)
.build();
// 表達(dá)式調(diào)度構(gòu)建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
scheduleBuilder.withMisfireHandlingInstructionDoNothing();
// 按新的cronExpression表達(dá)式構(gòu)建一個(gè)新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder)
.build();
if (!params.isEmpty()) {
// 放入?yún)?shù),運(yùn)行時(shí)的方法可以獲取
jobDetail.getJobDataMap().putAll(params);
}
Date date = scheduler.scheduleJob(jobDetail, trigger);
Xxl-job
說(shuō)明
XXL-JOB是一個(gè)分布式任務(wù)調(diào)度平臺(tái),其核心設(shè)計(jì)目標(biāo)是開(kāi)發(fā)迅速、學(xué)習(xí)簡(jiǎn)單、輕量級(jí)、易擴(kuò)展。
傳送門(mén)
官網(wǎng):
https://www.xuxueli.com/xxl-job/
gitee傳送門(mén):
https://gitee.com/xuxueli0323/xxl-job/tree/master
maven坐標(biāo)
<!-- 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>
快速入門(mén)
git clone https://gitee.com/xuxueli0323/xxl-job.git
獲得到目錄結(jié)構(gòu)
初始化數(shù)據(jù)庫(kù)
/xxl-job/doc/db/tables_xxl_job.sql
編譯源碼
xxl-job-admin:調(diào)度中心
xxl-job-core:公共依賴(lài)
xxl-job-executor-samples:執(zhí)行器Sample示例(選擇合適的版本執(zhí)行器,可直接使用,也可以參考其并將現(xiàn)有項(xiàng)目改造成執(zhí)行器)
:
xxl-job-executor-sample-springboot:Springboot版本,通過(guò)Springboot管理執(zhí)行器,推薦這種方式;
:
xxl-job-executor-sample-frameless:無(wú)框架版本;
配置部署調(diào)度中心
調(diào)度中心項(xiàng)目:xxl-job-admin
作用:統(tǒng)一管理任務(wù)調(diào)度平臺(tái)上的調(diào)度任務(wù),負(fù)責(zé)觸發(fā)調(diào)度執(zhí)行,并且提供任務(wù)管理平臺(tái)。
調(diào)度中心配置文件地址:
/xxl-job/xxl-job-admin/src/main/resources/Application.properties【修改數(shù)據(jù)庫(kù)配置】
/xxl-job/xxl-job-admin/src/main/resources/logback.xml
完成上述修改后,然后運(yùn)行XxlJobAdminApplication
運(yùn)行成功后通過(guò)瀏覽器打開(kāi):
http://localhost:8080/xxl-job-admin/,用戶(hù)名及密碼:admin/123456
至此“調(diào)度中心”項(xiàng)目已經(jīng)部署成功。
配置部署執(zhí)行器項(xiàng)目
執(zhí)行器項(xiàng)目:
xxl-job-executor-sample-springboot (提供多種版本執(zhí)行器供選擇,現(xiàn)以 springboot 版本為例,可直接使用,也可以參考其并將現(xiàn)有項(xiàng)目改造成執(zhí)行器)
作用:負(fù)責(zé)接收“調(diào)度中心”的調(diào)度并執(zhí)行;可以直接部署執(zhí)行器,也可以將執(zhí)行器集成到現(xiàn)有業(yè)務(wù)項(xiàng)目中。
執(zhí)行器配置
配置文件地址:
/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/
application.properties
修改:xxl.job.executor.logpath為本地路徑
logback.xml
修改日志路徑為本地路徑
新建任務(wù)
觸發(fā)執(zhí)行
請(qǐng)點(diǎn)擊任務(wù)右側(cè) “執(zhí)行” 按鈕,可手動(dòng)觸發(fā)一次任務(wù)執(zhí)行(通常情況下,通過(guò)配置Cron表達(dá)式進(jìn)行任務(wù)調(diào)度觸發(fā))。
查看日志
請(qǐng)點(diǎn)擊任務(wù)右側(cè) “日志” 按鈕,可前往任務(wù)日志界面查看任務(wù)日志。
在任務(wù)日志界面中,可查看該任務(wù)的歷史調(diào)度記錄以及每一次調(diào)度的任務(wù)調(diào)度信息、執(zhí)行參數(shù)和執(zhí)行信息。運(yùn)行中的任務(wù)點(diǎn)擊右側(cè)的“執(zhí)行日志”按鈕,可進(jìn)入日志控制臺(tái)查看實(shí)時(shí)執(zhí)行日志。
磁盤(pán)上的日志文件路徑在xxl.job.executor.logpath
更多
基礎(chǔ)配置:
- 執(zhí)行器:任務(wù)的綁定的執(zhí)行器,任務(wù)觸發(fā)調(diào)度時(shí)將會(huì)自動(dòng)發(fā)現(xiàn)注冊(cè)成功的執(zhí)行器, 實(shí)現(xiàn)任務(wù)自動(dòng)發(fā)現(xiàn)功能; 另一方面也可以方便的進(jìn)行任務(wù)分組。每個(gè)任務(wù)必須綁定一個(gè)執(zhí)行器, 可在 "執(zhí)行器管理" 進(jìn)行設(shè)置;
- 任務(wù)描述:任務(wù)的描述信息,便于任務(wù)管理;
- 負(fù)責(zé)人:任務(wù)的負(fù)責(zé)人;
- 報(bào)警郵件:任務(wù)調(diào)度失敗時(shí)郵件通知的郵箱地址,支持配置多郵箱地址,配置多個(gè)郵箱地址時(shí)用逗號(hào)分隔;
觸發(fā)配置:
- 調(diào)度類(lèi)型:
無(wú):該類(lèi)型不會(huì)主動(dòng)觸發(fā)調(diào)度;
CRON:該類(lèi)型將會(huì)通過(guò)CRON,觸發(fā)任務(wù)調(diào)度;
固定速度:該類(lèi)型將會(huì)以固定速度,觸發(fā)任務(wù)調(diào)度;按照固定的間隔時(shí)間,周期性觸發(fā);
固定延遲:該類(lèi)型將會(huì)以固定延遲,觸發(fā)任務(wù)調(diào)度;按照固定的延遲時(shí)間,從上次調(diào)度結(jié)束后開(kāi)始計(jì)算延遲時(shí)間,到達(dá)延遲時(shí)間后觸發(fā)下次調(diào)度;
- CRON:觸發(fā)任務(wù)執(zhí)行的Cron表達(dá)式;
- 固定速度:固件速度的時(shí)間間隔,單位為秒;
- 固定延遲:固件延遲的時(shí)間間隔,單位為秒;
任務(wù)配置:
- 運(yùn)行模式:
BEAN模式:任務(wù)以JobHandler方式維護(hù)在執(zhí)行器端;需要結(jié)合 "JobHandler" 屬性匹配執(zhí)行器中任務(wù);
GLUE模式(Java):任務(wù)以源碼方式維護(hù)在調(diào)度中心;該模式的任務(wù)實(shí)際上是一段繼承自IJobHandler的Java類(lèi)代碼并 "groovy" 源碼方式維護(hù),它在執(zhí)行器項(xiàng)目中運(yùn)行,可使用@Resource/@Autowire注入執(zhí)行器里中的其他服務(wù);
GLUE模式(Shell):任務(wù)以源碼方式維護(hù)在調(diào)度中心;該模式的任務(wù)實(shí)際上是一段 "shell" 腳本;
GLUE模式(Python):任務(wù)以源碼方式維護(hù)在調(diào)度中心;該模式的任務(wù)實(shí)際上是一段 "python" 腳本;
GLUE模式(php):任務(wù)以源碼方式維護(hù)在調(diào)度中心;該模式的任務(wù)實(shí)際上是一段 "php" 腳本;
GLUE模式(NodeJS):任務(wù)以源碼方式維護(hù)在調(diào)度中心;該模式的任務(wù)實(shí)際上是一段 "nodejs" 腳本;
GLUE模式(PowerShell):任務(wù)以源碼方式維護(hù)在調(diào)度中心;該模式的任務(wù)實(shí)際上是一段 "PowerShell" 腳本;
- JobHandler:運(yùn)行模式為 "BEAN模式" 時(shí)生效,對(duì)應(yīng)執(zhí)行器中新開(kāi)發(fā)的JobHandler類(lèi)“@JobHandler”注解自定義的value值;
- 執(zhí)行參數(shù):任務(wù)執(zhí)行所需的參數(shù);
高級(jí)配置:
- 路由策略:當(dāng)執(zhí)行器集群部署時(shí),提供豐富的路由策略,包括;
FIRST(第一個(gè)):固定選擇第一個(gè)機(jī)器;
LAST(最后一個(gè)):固定選擇最后一個(gè)機(jī)器;
ROUND(輪詢(xún)):;
RANDOM(隨機(jī)):隨機(jī)選擇在線的機(jī)器;
CONSISTENT_HASH(一致性HASH):每個(gè)任務(wù)按照Hash算法固定選擇某一臺(tái)機(jī)器,且所有任務(wù)均勻散列在不同機(jī)器上。
LEAST_FREQUENTLY_USED(最不經(jīng)常使用):使用頻率最低的機(jī)器優(yōu)先被選舉;
LEAST_RECENTLY_USED(最近最久未使用):最久未使用的機(jī)器優(yōu)先被選舉;
FAILOVER(故障轉(zhuǎn)移):按照順序依次進(jìn)行心跳檢測(cè),第一個(gè)心跳檢測(cè)成功的機(jī)器選定為目標(biāo)執(zhí)行器并發(fā)起調(diào)度;
BUSYOVER(忙碌轉(zhuǎn)移):按照順序依次進(jìn)行空閑檢測(cè),第一個(gè)空閑檢測(cè)成功的機(jī)器選定為目標(biāo)執(zhí)行器并發(fā)起調(diào)度;
SHARDING_BROADCAST(分片廣播):廣播觸發(fā)對(duì)應(yīng)集群中所有機(jī)器執(zhí)行一次任務(wù),同時(shí)系統(tǒng)自動(dòng)傳遞分片參數(shù);可根據(jù)分片參數(shù)開(kāi)發(fā)分片任務(wù);
- 子任務(wù):每個(gè)任務(wù)都擁有一個(gè)唯一的任務(wù)ID(任務(wù)ID可以從任務(wù)列表獲取),當(dāng)本任務(wù)執(zhí)行結(jié)束并且執(zhí)行成功時(shí),將會(huì)觸發(fā)子任務(wù)ID所對(duì)應(yīng)的任務(wù)的一次主動(dòng)調(diào)度。
- 調(diào)度過(guò)期策略:
- 忽略:調(diào)度過(guò)期后,忽略過(guò)期的任務(wù),從當(dāng)前時(shí)間開(kāi)始重新計(jì)算下次觸發(fā)時(shí)間;
- 立即執(zhí)行一次:調(diào)度過(guò)期后,立即執(zhí)行一次,并從當(dāng)前時(shí)間開(kāi)始重新計(jì)算下次觸發(fā)時(shí)間;
- 阻塞處理策略:調(diào)度過(guò)于密集執(zhí)行器來(lái)不及處理時(shí)的處理策略;
單機(jī)串行(默認(rèn)):調(diào)度請(qǐng)求進(jìn)入單機(jī)執(zhí)行器后,調(diào)度請(qǐng)求進(jìn)入FIFO隊(duì)列并以串行方式運(yùn)行;
丟棄后續(xù)調(diào)度:調(diào)度請(qǐng)求進(jìn)入單機(jī)執(zhí)行器后,發(fā)現(xiàn)執(zhí)行器存在運(yùn)行的調(diào)度任務(wù),本次請(qǐng)求將會(huì)被丟棄并標(biāo)記為失敗;
覆蓋之前調(diào)度:調(diào)度請(qǐng)求進(jìn)入單機(jī)執(zhí)行器后,發(fā)現(xiàn)執(zhí)行器存在運(yùn)行的調(diào)度任務(wù),將會(huì)終止運(yùn)行中的調(diào)度任務(wù)并清空隊(duì)列,然后運(yùn)行本地調(diào)度任務(wù);
- 任務(wù)超時(shí)時(shí)間:支持自定義任務(wù)超時(shí)時(shí)間,任務(wù)運(yùn)行超時(shí)將會(huì)主動(dòng)中斷任務(wù);
- 失敗重試次數(shù);支持自定義任務(wù)失敗重試次數(shù),當(dāng)任務(wù)失敗時(shí)將會(huì)按照預(yù)設(shè)的失敗重試次數(shù)主動(dòng)進(jìn)行重試;
你看,奇怪的知識(shí)又增加了!