Quartz的优点介绍:
Quartz支持Cron表达式定义时间点也支持SimpleTrigger对应时间点,可以很精确的定义时间点。Quartz支持集群,可以在多个服务器(连同一个数据库)自动分配到不同的服务器上执行。Quartz支持多种错误处理形式(如错误后下次不执行、马上重新执行、下次继续执行等)Quartz支持多种漏触发处理(如关机漏触发情况)Quartz还有很多优点
Timer 和 Quartz 的区别:
1、Timer 只能执行定时定频率的任务,而 Quartz 并不只局限与此功能,它也可以根据日历来触发。
2、Timer 只有一个线程在执行,而 Quartz 有线程池,可开启多个线程。
3、Timer 中出现异常,一切 GG,不能记录事故现场,而 Quartz 可以。
简单的Quartz实现:
1.相关的依赖:
<!--Quartz任务调度--> |
2.重要的三个类
1)Job 工作/任务类(用来规定具体要执行的事件)
2)Trigger 触发器(用来规定执行的规则,如何时执行,如何执行)触发器主要了解两个类
SimpleTrigger 简单触发器 能指定比较简单触发时间,例如:每隔5秒执行
CronTrigger 复杂的触发任务
3)Schedule 调度器(用来绑定任务和触发器,并开始执行)
工作类的实现:主要就是实现Job接口,实现里面的execute方法。
/** 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) { //定义一个Trigger,触发条件类 //定义一个JobDetail //启动任务调度 |
获得Scheduler调度器的方式有两种:
方式一:通过new 的方式创建 StdSchedulerFactory对象,并通过getScheduler方法获取 方式二:通过StdSchedulerFactory类的静态方法getDefaultScheduler直接获取 |
以下是仅作为了解的Trigger:
CalendarIntervalTrigger 类似于SimpleTrigger,指定从某一个时间开始,以一定的时间间隔执行的任务。 它适合的任务类似于:9:00 开始执行,并且以后每周 9:00 执行一次 DailyTimeIntervalTrigger 指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。 它的属性有: DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule() |
主要的两种Trigger为SimpleTrigger和CronTrigger
Cron表达式(6-7位) | 位置 | 时间域 | 允许值 | 特殊值 | 星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如, 在分钟字段时,表示“每分钟”; 问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符; 减号(-):表达一个范围,如在小时字段中使用“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,加入属性值,方式有两种: 第二种:【通过JobDetail对象获取JobDataMap然后put值】 在Job中可以获取这个JobDataMap的值,方式同样有两种【一般使用第一种】: //方法一:获得JobDataMap private String 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; |
还需要进行配置:
@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)。