任務排程-Quartz
我們常用的任務排程有:
springTask:spring自帶的,使用@Scheduled註解就可以簡單快速的實現一個任務
Quartz:是一個非常成熟的任務排程工具,可以精確到毫秒級別, 獨立執行,可以整合到容器中, 支援事務(JobStoreCMT ), 支援叢集 ,支援持久化
xxx-job:分散式的任務排程,以前基於Quartz
Elastic-Job:分散式的任務排程,基於Quartz
1、基本結構
依賴:
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency>
springboot中
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
2、關鍵角色
2.1 Job:任務物件,任務執行的內容,我們寫的任務需要實現Job介面,context可以獲取到JobDetail中攜帶的資訊
public class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("觸發器資訊>>>"+context.getTrigger()); System.out.println("觸發器名字>>>"+context.getTrigger().getKey().getName()); System.out.println("觸發器組名>>>"+context.getTrigger().getKey().getGroup()); System.out.println("任務建立人>>>"+context.getMergedJobDataMap().get("creater")); System.out.println("任務建立時間 >>>"+context.getMergedJobDataMap().get("createTime")); System.out.println("任務建立內容 >>>現在時間"+ LocalDateTime.now()); } }
JobDetail用來包裝job,指定jobName和groupName組成的唯一標識,jobDataMap可以攜帶kv資料,context可以獲取資訊
JobDetail jobDetail = JobBuilder.newJob(MyJob.class) //組成唯一標識 .withIdentity("jobkey","groupkey") //JobData攜帶資料,可在context中獲取 .usingJobData("creater","jack") .usingJobData("createTime","2020/11/4") .build();
2.2、Trigger:觸發器,有不同的規則來觸發任務的執行。
SimpleTrigger:簡單觸發器, 固定時刻或時間間隔,毫秒
CalendarIntervalTrigger: 基於日曆的觸發器,可以根據月份、年份來觸發,天數和年數不固定也適用,我們可以不需要去計算時間間隔。
DailyTimeIntervalTrigger: 基於日期的觸發器 每天的某個時間段,每週末哪幾天觸發
CronTrigger :基於 Cron 表示式的觸發器,全能 ,線上cron生成器https://qqe2.com/cron
比如這個simpletrigger,每兩秒觸發任務
ScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(2) .repeatForever(); Trigger trigger = TriggerBuilder.newTrigger()
//唯一標識 .withIdentity("simpletrigger1","simpletrigger1")
//立刻啟動,可以指定某個時間 .startNow()
//可以將上面的直接放在這裡面 .withSchedule(simpleScheduleBuilder) .build();
2.3Scheduler:排程器,由 StdSchedulerFactory 產生,單例的,可以啟動和停止任務,操作trigger和job
SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.scheduleJob(jobDetail,trigger); scheduler.start();
2.4Listener:監聽器,Quartz 中提供了三種 Listener,監聽 Scheduler 的,監聽 Trigger 的,監聽 Job 的。
我們只需要實現相應的介面,然後在排程器中註冊,既可以實現功能。
JobListener:
public class implements JobListener { @Override public String getName() { //返回JobListener的名字 return "JobListenerTest"; } @Override public void jobToBeExecuted(JobExecutionContext context) { System.out.println("任務呼叫前執行。。。"); } @Override public void jobExecutionVetoed(JobExecutionContext context) { System.out.println("任務呼叫前,又被TriggerListener否決了執行。。。"); } @Override public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { System.out.println("任務呼叫後執行。。。"); } }
排程器中註冊:
SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.scheduleJob(jobDetail,trigger); //註冊JobListener scheduler.getListenerManager().addJobListener(new JobListenerTest()); scheduler.start();
執行結果:
TriggerListener:也是在排程器中註冊;
public class TriggerListenerTest implements TriggerListener { @Override public String getName() { //返回監聽器的名稱 return "TriggerListenerTest"; } @Override public void triggerFired(Trigger trigger, JobExecutionContext context) { //Trigger 被觸發,Job 上的 execute() 方法將要被執行時,Scheduler 就呼叫這個 //方法 System.out.println("Trigger 被觸發"); } @Override public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { /* 在 Trigger 觸發後 , Job 將被執行前時 由 Scheduler呼叫這個方 法 。 TriggerListener 給了一個選擇去否決Job 的執行。假如這個方法返回true,這 個 Job 將不會為此次 Trigger 觸發而得到執行,預設為false*/ return false; } @Override public void triggerMisfired(Trigger trigger) { //Trigger 錯過觸發時呼叫 System.out.println("Trigger 錯過觸發時呼叫"); } @Override public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) { //Trigger 被觸發並且完成了 Job 的執行時 System.out.println("Trigger 被觸發並且完成了 Job 的執行"); } }
任務執行結果:trigger的triggerFired()在JobListenerTest的jobToBeExecuted()呼叫之前執行,
3、Calendar 排除規則
如果要在觸發器的基礎上,排除一些時間區間不執行任務,就要用到 Quartz 的 Calendar 類,可以按年、月、周、日、特定日期、Cron 表示式排除。
AnnualCalendar:排除年中一天或多天
WeeklyCalendar:排除一個星期中的周幾,例如排除週末,預設週六和週日
HolidayCalendar:排除節假日,如國家法定節假日
CronCalendar:排除了由給定的 Cron表示式的時間集合
DailyCalendar:排除某個時間段,比如排除非9:00-17:00的時間
MonthlyCalendar:排除月份中的指定數天,例除排除每月的最後一天或第一天
使用:
public static void main(String[] args) throws SchedulerException, ParseException { JobDetail jobDetail = JobBuilder.newJob(MyJob.class) .usingJobData("creater","suwu") .usingJobData("createTime","2020/11/11") .build(); SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(2) .repeatForever();
//排除一個星期中的周幾,3表示星期二,1表示星期日 WeeklyCalendar weeklyCalendar = new WeeklyCalendar(); weeklyCalendar.setDayExcluded(3,true); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("WeeklyCalendar", "WeeklyCalendar") .startNow() // .startAt(date) .modifiedByCalendar("weekly2") .withSchedule(scheduleBuilder) .build(); SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.scheduleJob(jobDetail,trigger); scheduler.addCalendar("weekly2",weeklyCalendar,true,true); scheduler.start(); }
4、任務儲存
Jobstore 用來儲存任務和觸發器相關的資訊,例如所有任務的名稱、數量、狀態等等,可以用來做視覺化介面的管理
Quartz 中有兩種儲存任務的方式,一種在在記憶體,一種是在資料庫,預設是在記憶體中RAMJobstore,存在記憶體中
的壞處就是系統重啟後,儲存在記憶體中的資料都會丟失,所以一般採用持久化處理,存在資料庫JDBCJobStore中
1、首先我們需要配置資料庫的資訊:
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 使用 quartz.properties,不使用預設配置 org.quartz.jobStore.useProperties:false #資料庫中 quartz 表的表名字首 org.quartz.jobStore.tablePrefix:QRTZ_ org.quartz.jobStore.dataSource:myQuartz #配置資料來源 org.quartz.dataSource.myQuartz.driver:com.mysql.jdbc.Driver org.quartz.dataSource.myQuartz.URL:jdbc:mysql://localhost:3306/boot?useUnicode=true&characterEncoding=utf8 org.quartz.dataSource.myQuartz.user:root org.quartz.dataSource.myQuartz.password:123456 org.quartz.dataSource.myQuartz.validationQuery=select 0 from dual
org.quartz.threadPool.threadCount: 10
org.quartz.jobStore.misfireThreshold:10
這個配置在quartz.properties的檔案中,自己建立。
quartz會預設載入org.quartz下的quartz.properties檔案,這裡覆蓋了就載入我們自己建立的檔案,檔案需要同名。
預設是RAMJobStore
org.quartz.scheduler.instanceName: DefaultQuartzScheduler org.quartz.scheduler.rmi.export: false org.quartz.scheduler.rmi.proxy: false org.quartz.scheduler.wrapJobExecutionInUserTransaction: false org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount: 10 org.quartz.threadPool.threadPriority: 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true org.quartz.jobStore.misfireThreshold: 60000 org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
2、建立表
quartz已經提供了不同資料庫的建表語句
mysql的建表語句:
# # In your Quartz properties file, you'll need to set # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate # # # By: Ron Cordell - roncordell # I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM. DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR(190) NOT NULL, JOB_GROUP VARCHAR(190) NOT NULL, DESCRIPTION VARCHAR(250) NULL, JOB_CLASS_NAME VARCHAR(250) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_NONCONCURRENT VARCHAR(1) NOT NULL, IS_UPDATE_DATA VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) NOT NULL, JOB_NAME VARCHAR(190) NOT NULL, JOB_GROUP VARCHAR(190) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT(13) NULL, PREV_FIRE_TIME BIGINT(13) NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(190) NULL, MISFIRE_INSTR SMALLINT(2) NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) NOT NULL, REPEAT_COUNT BIGINT(7) NOT NULL, REPEAT_INTERVAL BIGINT(12) NOT NULL, TIMES_TRIGGERED BIGINT(10) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) NOT NULL, CRON_EXPRESSION VARCHAR(120) NOT NULL, TIME_ZONE_ID VARCHAR(80), PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INT NULL, INT_PROP_2 INT NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 VARCHAR(1) NULL, BOOL_PROP_2 VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) NOT NULL, BLOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR(190) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)) ENGINE=InnoDB; CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR(190) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) NOT NULL, INSTANCE_NAME VARCHAR(190) NOT NULL, FIRED_TIME BIGINT(13) NOT NULL, SCHED_TIME BIGINT(13) NOT NULL, PRIORITY INTEGER NOT NULL, STATE VARCHAR(16) NOT NULL, JOB_NAME VARCHAR(190) NULL, JOB_GROUP VARCHAR(190) NULL, IS_NONCONCURRENT VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,ENTRY_ID)) ENGINE=InnoDB; CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR(190) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)) ENGINE=InnoDB; CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME)) ENGINE=InnoDB; CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY); CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME); CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME); CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); commit;View Code
3、執行程式,即可在資料庫中查詢到任務的儲存資料
JDBC 的實現方式有兩種,JobStoreSupport 類的兩個子類:
JobStoreTX:在獨立的程式中使用,自己管理事務,不參與外部事務。
JobStoreCMT:如果需要容器管理事 務時,使用它。
當前專案使用的是JobStoreTX
Quartz錯過觸發的處理:https://www.cnblogs.com/daxin/p/3919927.html