我们常用的任务调度有:
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.3 Scheduler:调度器,由 StdSchedulerFactory 产生,单例的,可以启动和停止任务,操作trigger和job
SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.scheduleJob(jobDetail,trigger); scheduler.start();
2.4 Listener:监听器,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;
3、运行程序,即可在数据库中查询到任务的储存数据
JDBC 的实现方式有两种,JobStoreSupport 类的两个子类:
JobStoreTX:在独立的程序中使用,自己管理事务,不参与外部事务。
JobStoreCMT:如果需要容器管理事 务时,使用它。
当前项目使用的是JobStoreTX
Quartz错过触发的处理: https://www.cnblogs.com/daxin/p/3919927.html