quartz定时任务调度系统的应用

Quartz的优点介绍:

Quartz支持Cron表达式定义时间点也支持SimpleTrigger对应时间点,可以很精确的定义时间点。Quartz支持集群,可以在多个服务器(连同一个数据库)自动分配到不同的服务器上执行。Quartz支持多种错误处理形式(如错误后下次不执行、马上重新执行、下次继续执行等)Quartz支持多种漏触发处理(如关机漏触发情况)Quartz还有很多优点

Timer 和 Quartz 的区别:

1、Timer 只能执行定时定频率的任务,而 Quartz 并不只局限与此功能,它也可以根据日历来触发。

2、Timer 只有一个线程在执行,而 Quartz 有线程池,可开启多个线程。

3、Timer 中出现异常,一切 GG,不能记录事故现场,而 Quartz 可以。

简单的Quartz实现:
1.相关的依赖:

<!--Quartz任务调度-->
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>


2.重要的三个类
1)Job 工作/任务类(用来规定具体要执行的事件)
2)Trigger 触发器(用来规定执行的规则,如何时执行,如何执行)触发器主要了解两个类
SimpleTrigger 简单触发器 能指定比较简单触发时间,例如:每隔5秒执行
CronTrigger 复杂的触发任务
3)Schedule 调度器(用来绑定任务和触发器,并开始执行)

工作类的实现:主要就是实现Job接口,实现里面的execute方法。

/**
* 工作类的具体实现
* */
public class HelloQuartz implements Job {
//执行
public void execute(JobExecutionContext context) throws JobExecutionException {
//创建工作详情
JobDetail detail=context.getJobDetail();
//获取工作的名称
String name=detail.getJobDataMap().getString("name");
String job=detail.getJobDataMap().getString("job1");

System.out.println("任务调度:组:"+job+",工作名:"+name+"---->今日整点抢购,不容错过!");
}
}


运行调度任务的具体过程如下:
JobDetail和Trigger都有name和group。【用来区分不同的任务和触发器,在实际操作中可以不需要group,它可能会放入一个默认的group中】
name是它们在这个sheduler里面的唯一标识。如果我们要更新一个JobDetail定义,只需要 
设置一个name相同的JobDetail实例即可。
group是一个组织单元,sheduler会提供一些对整组操作的API,比如
scheduler.resumeJobs()。

public static void main(String[] args) {
try{
//创建scheduler,执行计划
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

//定义一个Trigger,触发条件类
Trigger trigger = TriggerBuilder.newTrigger().
withIdentity("trigger1", "group1") //定义name/group
.startNow()//一旦加入scheduler,立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule() //使用SimpleTrigger
.withIntervalInSeconds(1) //每隔一秒执行一次
.repeatForever()) //一直执行,奔腾到老不停歇
.build();

//定义一个JobDetail
JobDetail job = JobBuilder.newJob(HelloQuartz.class) //定义Job类为HelloQuartz类,这是真正的执行逻辑所在
.withIdentity("job1", "group1") //定义name/group
.usingJobData("name", "quartz") //定义属性
.build();
//加入这个调度
scheduler.scheduleJob(job, trigger);

//启动任务调度
scheduler.start();

}catch (Exception ex){
ex.printStackTrace();
}
}


获得Scheduler调度器的方式有两种:

方式一:通过new 的方式创建 StdSchedulerFactory对象,并通过getScheduler方法获取
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = stdSchedulerFactory.getScheduler();

方式二:通过StdSchedulerFactory类的静态方法getDefaultScheduler直接获取
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();


以下是仅作为了解的Trigger:

CalendarIntervalTrigger

类似于SimpleTrigger,指定从某一个时间开始,以一定的时间间隔执行的任务。
但是不同的是SimpleTrigger指定的时间间隔为毫秒,没办法指定每隔一个月执行一次(每月的时间间隔不是固定值),而CalendarIntervalTrigger支持的间隔单位有秒,分钟,小时,天,月,年,星期。
相较于SimpleTrigger有两个优势:
1、更方便,比如每隔1小时执行,你不用自己去计算1小时等于多少毫秒。
2、支持不是固定长度的间隔,比如间隔为月和年。但劣势是精度只能到秒。

它适合的任务类似于:9:00 开始执行,并且以后每周 9:00 执行一次
它的属性有:
interval 执行间隔
intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)

DailyTimeIntervalTrigger

指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。
它适合的任务类似于:指定每天9:00 至 18:00 ,每隔70秒执行一次,并且只要周一至周五执行。

它的属性有:
startTimeOfDay 每天开始时间
endTimeOfDay 每天结束时间
daysOfWeek 需要执行的星期
interval 执行间隔
intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)
repeatCount 重复次数

DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()
.startingDailyAt(TimeOfDay.hourAndMinuteOfDay(9, 0)) //每天9:00开始
.endingDailyAt(TimeOfDay.hourAndMinuteOfDay(18, 0)) //18:00 结束
//周一至周五执行
.onDaysOfTheWeek(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY)
.withIntervalInHours(1) //每间隔1小时执行一次
.withRepeatCount(100) //最多重复100次(实际执行100+1次)
.build();

主要的两种Trigger为SimpleTrigger和CronTrigger

Cron表达式(6-7位)

| 位置 | 时间域 | 允许值 | 特殊值 |
| ------- | -------------| ---------- | --------------- |
| 1 | 秒 | 0-59 | , - * / |
| 2 | 分钟 | 0-59 | , - * / |
| 3 | 小时 | 0-23 | , - * / |
| 4 | 日期 | 1-31 | , - * ? / L W C |
| 5 | 月份 | 1-12 | , - * / |
| 6 | 星期 | 1-7 | , - * ? / L C # |
| 7 | 年份(可选)| 1-31 | , - * / |

星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如, 在分钟字段时,表示“每分钟”;

问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;

减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;

逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;

斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在秒字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;

L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;

W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;

LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;

井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;

C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。

 

JobDataMap【向任务(Job)传递值】

Job都次都是newInstance的实例,那我怎么传值给它? 比如我现在有两个发送邮件的任务,一个是发给"liLei",一个发给"hanmeimei",不能说我要写两个Job实现类LiLeiSendEmailJob和HanMeiMeiSendEmailJob。实现的办法是通过JobDataMap。

每一个JobDetail都会有一个JobDataMap。JobDataMap本质就是一个Map的扩展类,只是提供了一些更便捷的方法,比如getString()之类的。

我们可以在定义JobDetail,加入属性值,方式有两种:
第一种:【直接使用里面的usingJobData方法通过键值对的方式】
newJob().usingJobData("age", 18) //加入属性到ageJobDataMap

第二种:【通过JobDetail对象获取JobDataMap然后put值】
job.getJobDataMap().put("name", "quertz"); //加入属性name到JobDataMap

在Job中可以获取这个JobDataMap的值,方式同样有两种【一般使用第一种】:

//方法一:获得JobDataMap
JobDetail detail = context.getJobDetail();
JobDataMap map = detail.getJobDataMap();

private String name;
//方法二:属性的setter方法,会将JobDataMap的属性自动注入
public void setName(String name) {
this.name = name;
}

对于同一个JobDetail实例,执行的多个Job实例,是共享同样的JobDataMap,也就是说,如果你在任务里修改了里面的值,会对其他Job实例(并发的或者后续的)造成影响。

除了JobDetail,Trigger同样有一个JobDataMap,共享范围是所有使用这个Trigger的Job实例

需要编写好需要执行的类

springboot中使用:

持久化:将触发器及任务等信息进行存储,如果出现程序关闭的情况,在下次启动的时候会自动启动已经持久化的任务

参考网址:https://blog.csdn.net/u012954706/article/details/79671442

依赖:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>


quartz持久化之前需要先创建几张表,一下是mysql的语法:

持久化建表语句:https://blog.csdn.net/u010077555/article/details/83988335

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(200) NOT NULL,
JOB_GROUP VARCHAR(200) 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)
);

CREATE TABLE QRTZ_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) 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(200) 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)
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) 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)
);

CREATE TABLE QRTZ_CRON_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(200) 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)
);

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) 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)
);

CREATE TABLE QRTZ_BLOB_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB 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)
);

CREATE TABLE QRTZ_CALENDARS
(
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(200) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) 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(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);

CREATE TABLE QRTZ_SCHEDULER_STATE
(
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
(
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);
commit;

还需要进行配置:

@Configuration
public class QuartzConfig {

 @Bean
 public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("dataSource") DataSource dataSource){

 //任务的持久化保存到数据库中
 //quartz参数
 Properties prop = new Properties();
 //配置实例
 prop.put("org.quartz.scheduler.instanceName", "MyScheduler");//实例名称
 prop.put("org.quartz.scheduler.instanceId", "AUTO");
 //线程池配置
 prop.put("org.quartz.threadPool.threadCount", "5");
 //JobStore配置
 prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
 prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");//表前缀


 SchedulerFactoryBean schedulerFactoryBean =  new SchedulerFactoryBean();
 schedulerFactoryBean.setDataSource(dataSource);
 schedulerFactoryBean.setQuartzProperties(prop);
 schedulerFactoryBean.setStartupDelay(5);//设置延迟启动时间
 schedulerFactoryBean.setAutoStartup(true);//设置任务自动执行

 return schedulerFactoryBean;
 }
}

quartz.properties配置文件详解,参考:https://blog.csdn.net/qq_39669058/article/details/90443857


使用可以通过@Autowired 方式进行注入,来使用

即:

@Autowired

private Scheduler scheduler;

其他的使用方法,跟简单的类似

 

Quartz集群实现任务调度:

主要是在持久化的基础上,添加相关的配置。

参考的配置网址:https://my.oschina.net/u/3164861/blog/1812663

来自网上记录:

Quartz Scheduler在集群中的启动流程

  Quartz Scheduler自身是察觉不到被集群的,只有配置给Scheduler的JDBC JobStore才知道。当Quartz Scheduler启动时,它调用JobStore的schedulerStarted()方法,它告诉JobStore Scheduler已经启动了。schedulerStarted() 方法是在JobStoreSupport类中实现的。JobStoreSupport类会根据quartz.properties文件中的设置来确定Scheduler实例是否参与到集群中。假如配置了集群,一个新的ClusterManager类的实例就被创建、初始化并启动。ClusterManager是在JobStoreSupport类中的一个内嵌类,继承了java.lang.Thread,它会定期运行,并对Scheduler实例执行检入的功能。Scheduler也要查看是否有任何一个别的集群节点失败了。检入操作执行周期在quartz.properties中配置。

侦测失败的Scheduler节点

  当一个Scheduler实例执行检入时,它会查看是否有其他的Scheduler实例在到达他们所预期的时间还未检入。这是通过检查SCHEDULER_STATE表中Scheduler记录在LAST_CHEDK_TIME列的值是否早于org.quartz.jobStore.clusterCheckinInterval来确定的。如果一个或多个节点到了预定时间还没有检入,那么运行中的Scheduler就假定它(们) 失败了。

从故障实例中恢复Job

  当一个Sheduler实例在执行某个Job时失败了,有可能由另一正常工作的Scheduler实例接过这个Job重新运行。要实现故障实例恢复,配置给JobDetail对象的Job可恢复属性必须设置为true(job.setRequestsRecovery(true))。如果可恢复属性被设置为false(默认为false),当某个Scheduler在运行该job失败时,它将不会重新运行;而是由另一个Scheduler实例在下一次触发时间触发。Scheduler实例出现故障后多快能被侦测到取决于每个Scheduler的检入间隔(即org.quartz.jobStore.clusterCheckinInterval)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值