网站分析报告怎么写,友情链接的作用大不大,网页升级防问广大,seo百度关键词优化软件文章目录 一、Quartz 基本介绍二、Quartz Java 编程1、文档2、引入依赖3、入门案例4、默认配置文件 三、Quartz 重要组件1、Quartz架构体系2、JobDetail3、Trigger#xff08;1#xff09;代码实例#xff08;2#xff09;SimpleTrigger#xff08;3#xff09;CalendarI… 文章目录 一、Quartz 基本介绍二、Quartz Java 编程1、文档2、引入依赖3、入门案例4、默认配置文件 三、Quartz 重要组件1、Quartz架构体系2、JobDetail3、Trigger1代码实例2SimpleTrigger3CalendarIntervalTrigger4DailyTimeIntervalTrigger5CronTrigger6基于 Calendar 的排除规则 4、Scheduler5、Listener1JobListener2TriggerListener3SchedulerListener 6、JobStore1RAMJobStore2JDBCJobStore 四、Quartz集成Spring1、xml配置2、注解配置 五、使用Quartz实现动态调度六、Quartz 集群部署1、为什么需要集群2、集群需要解决的问题3、集群配置与验证 七、Quartz 调度原理与源码分析 一、Quartz 基本介绍
官网http://www.quartz-scheduler.org/ 源码地址https://github.com/quartz-scheduler/quartz
Quartz 的意思是石英像石英表一样精确。 Quartz is a richly featured, open source job scheduling library that can be integrated within virtually any Java application - from the smallest stand-alone application to the largest e-commerce system. Quartz can be used to create simple or complex schedules for executing tens, hundreds, or even tens-of-thousands of jobs; jobs whose tasks are defined as standard Java components that may execute virtually anything you may program them to do. The Quartz Scheduler includes many enterprise-class features, such as support for JTA transactions and clustering. Quatz 是一个特性丰富的开源的任务调度库它几乎可以嵌入所有的 Java 程序从很小的独立应用程序到大型商业系统。Quartz 可以用来创建成百上千的简单的或者复杂的任务这些任务可以用来执行任何程序可以做的事情。Quartz 拥有很多企业级的特性包括支持 JTA 事务和集群。
Quartz 是一个老牌的任务调度系统98 年构思01 年发布到 sourceforge。现在更新比较慢因为已经非常成熟了。
Quartz 的目的就是让任务调度更加简单开发人员只需要关注业务即可。他是用 Java 语言编写的也有.NET 的版本。Java 代码能做的任何事情Quartz 都可以调度。
特点 精确到毫秒级别的调度 可以独立运行也可以集成到容器中 支持事务JobStoreCMT 支持集群 支持持久化
二、Quartz Java 编程
1、文档
http://www.quartz-scheduler.org/documentation/quartz-2.3.0/ http://www.quartz-scheduler.org/documentation/quartz-2.3.0/quick-start.html
2、引入依赖
dependencygroupIdorg.quartz-scheduler/groupIdartifactIdquartz/artifactIdversion2.3.0/version
/dependency3、入门案例
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
// 定义job实现Job接口
public class MyJob1 implements Job {public void execute(JobExecutionContext context) throws JobExecutionException {Date date new Date();SimpleDateFormat sf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss);// 获取job的自定义参数JobDataMap dataMap context.getJobDetail().getJobDataMap();System.out.println( sf.format(date) 任务1执行了 dataMap.getString(cxf));}
}import com.test.job.MyJob1;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
// 创建定时任务
public class MyScheduler {public static void main(String[] args) throws SchedulerException {// JobDetail 把 Job 进一步包装成 JobDetailJobDetail jobDetail JobBuilder.newJob(MyJob1.class)// 必须要指定 JobName 和 groupName两个合起来是唯一标识符.withIdentity(job1, group1) // 任务名 任务组 同一个组中包含许多任务// 可以携带 KV 的数据JobDataMap用于扩展属性在运行的时候可以从 context获取到.usingJobData(cxf,加油) // 添加额外自定义参数.usingJobData(moon,5.21F).build();// TriggerTrigger trigger TriggerBuilder.newTrigger().withIdentity(trigger1, group1) // 定义trigger名 组名.startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule() // 简单触发器.withIntervalInSeconds(2) // 2秒一次.repeatForever()) // 持续不断执行.build();// SchedulerFactorySchedulerFactory factory new StdSchedulerFactory();// Scheduler 一定是单例的Scheduler scheduler factory.getScheduler();// 绑定关系是1N 把 JobDetail 和 Trigger绑定注册到容器中scheduler.scheduleJob(jobDetail, trigger);// Scheduler 先启动后启动无所谓只要有 Trigger 到达触发条件就会执行任务scheduler.start();}
}4、默认配置文件
org.quartz包下有一个默认的配置文件quartz.properties。当我们没有定义一个同名的配置文件的时候就会使用默认配置文件里面的配置。
想要覆盖默认的配置在resources目录下新建quartz.properties即可。
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
# 调度器名称
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: trueorg.quartz.jobStore.misfireThreshold: 60000org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
三、Quartz 重要组件
在线生成cron表达式网站https://cron.qqe2.com/
1、Quartz架构体系 2、JobDetail
我们创建一个实现 Job 接口的类使用 JobBuilder 包装成 JobDetail它可以携带KV 的数据。
// JobDetail
JobDetail jobDetail JobBuilder.newJob(MyJob1.class).withIdentity(job1, group1) // 任务名 任务组 同一个组中包含许多任务.usingJobData(cxf,加油) // 添加额外自定义参数.usingJobData(moon,5.21F).build();3、Trigger
定义任务的触发规律Trigger使用 TriggerBuilder 来构建。 JobDetail 跟 Trigger 是 1:N 的关系。
Trigger 接口在 Quartz 有 4 个继承的子接口
子接口描述特点SimpleTrigger简单触发器固定时刻或时间间隔毫秒CalendarIntervalTrigger基于日历的触发器比简单触发器更多时间单位支持非固定时间的触发例如一年可能 365/366天一个月可能 28/29/30/31天DailyTimeIntervalTrigger基于日期的触发器每天的某个时间段CronTrigger基于 Cron 表达式的触发器
其中MutableTrigger 和 CoreTrigger 最终也是用到以上四个类的实现类。
1代码实例
/*** CalendarIntervalTrigger*/
Trigger calendarIntervalTrigger TriggerBuilder.newTrigger().withIdentity(trigger1, test).startNow().withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withIntervalInDays(1)) // 每天执行一次.build();/*** DailyTimeIntervalTrigger*/
Trigger dailyTimeIntervalTrigger TriggerBuilder.newTrigger().withIdentity(trigger1, test).startNow().withSchedule(DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule().startingDailyAt(TimeOfDay.hourAndMinuteOfDay(9, 0)) //第天900开始.endingDailyAt(TimeOfDay.hourAndMinuteOfDay(16, 0)) //1600 结束.onDaysOfTheWeek(1,2,3,4,5) //周一至周五执行.withIntervalInHours(1) //每间隔1小时执行一次.withRepeatCount(100))//最多重复100次实际执行1001次.build();
2SimpleTrigger
SimpleTrigger 可以定义固定时刻或者固定时间间隔的调度规则精确到毫秒。 例如每天 9 点钟运行每隔 30 分钟运行一次。
3CalendarIntervalTrigger
CalendarIntervalTrigger 可以定义更多时间单位的调度需求精确到秒。 好处是不需要去计算时间间隔比如 1 个小时等于多少毫秒。 例如每年、每个月、每周、每天、每小时、每分钟、每秒。 每年的月数和每个月的天数不是固定的这种情况也适用。
4DailyTimeIntervalTrigger
每天的某个时间段内以一定的时间间隔执行任务。 例如每天早上 9 点到晚上 9 点每隔半个小时执行一次并且只在周一到周六执行。
5CronTrigger
CronTirgger 可以定义基于 Cron 表达式的调度规则是最常用的触发器类型。
Cron 表达式 星号(*)可用在所有字段中表示对应时间域的每一个时刻例如在分钟字段时表示“每分钟” 问号?该字符只在日期和星期字段中使用它通常指定为“无意义的值”相当于点位符 减号(-)表达一个范围如在小时字段中使用“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 在星期字段中相当于星期日后的第一天。
Cron 表达式对特殊字符的大小写不敏感对代表星期的缩写英文大小写也不敏感。
6基于 Calendar 的排除规则
我们有一些在什么时间不执行的需求比如理财周末和法定假日购买不计息证券公司周末和法定假日休市。需要怎么实现呢
如果要在触发器的基础上排除一些时间区间不执行任务就要用到 Quartz 的Calendar 类注意不是 JDK 的 Calendar。可以按年、月、周、日、特定日期、Cron表达式排除。 调用 Trigger 的 modifiedByCalendar()添加到触发器中并且调用调度器的addCalendar()方法注册排除规则。
Calendar名称用法BaseCalendar为高级的 Calendar 实现了基本的功能实现了 org.quartz.Calendar 接口AnnualCalendar排除年中一天或多天CronCalendar日历的这种实现排除了由给定的 CronExpression 表达的时间集合。 例如您可以使用此日历使用表达式“* * 0-7,18-23* *”每天排除所有营业时间上午 8 点至下午 5 点。 如果 CronTrigger 具有给定的 cron 表达式并且与具有相同表达式的 CronCalendar 相关联则日历将排除触发器包含的所有时间并且它们将彼此抵消。DailyCalendar您可以使用此日历来排除营业时间上午 8 点 - 5 点每天。 每个DailyCalendar 仅允许指定单个时间范围并且该时间范围可能不会跨越每日边界即您不能指定从上午 8 点至凌晨 5 点的时间范围。 如果属性 invertTimeRange 为 false默认则时间范围定义触发器不允许触发的时间范围。 如果 invertTimeRange 为 true则时间范围被反转 - 也就是排除在定义的时间范围之外的所有时间。HolidayCalendar特别的用于从 Trigger 中排除节假日MonthlyCalendar排除月份中的指定数天例如可用于排除每月的最后一天WeeklyCalendar排除星期中的任意周几例如可用于排除周末默认周六和周日
// 代码实例
public class CalendarDemo {public static void main(String[] args) throws Exception {SchedulerFactory sf new StdSchedulerFactory();Scheduler scheduler sf.getScheduler();scheduler.start();// 定义日历AnnualCalendar holidays new AnnualCalendar();// 排除某一日Calendar testDay (Calendar) new GregorianCalendar(2023, 8, 8);holidays.setDayExcluded(testDay, true);// 排除中秋节Calendar midAutumn new GregorianCalendar(2023, 9, 29);holidays.setDayExcluded(midAutumn, true);// 排除圣诞节Calendar christmas new GregorianCalendar(2023, 12, 25);holidays.setDayExcluded(christmas, true);// 调度器添加日历scheduler.addCalendar(holidays, holidays, false, false);JobDetail jobDetail JobBuilder.newJob(MyJob1.class).withIdentity(job1, test).usingJobData(cxf,加油).build();Trigger trigger TriggerBuilder.newTrigger().withIdentity(trigger1, test).startNow().modifiedByCalendar(holidays).withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();Date firstRunTime scheduler.scheduleJob(jobDetail, trigger);System.out.println(jobDetail.getKey() 第一次触发 firstRunTime);}
}
4、Scheduler
Scheduler就是Quartz的调度器是 Quartz 的指挥官由 StdSchedulerFactory 产生。它是单例的。并且是 Quartz 中最重要的 API默认是实现类是 StdScheduler里面包含了一个QuartzScheduler。QuartzScheduler 里面又包含了一个 QuartzSchedulerThread。 Scheduler 中的方法主要分为三大类这些方法非常重要可以实现任务的动态调度 1操作调度器本身例如调度器的启动 start()、调度器的关闭 shutdown()。 2操作 Trigger例如 pauseTriggers()、resumeTrigger()。 3操作 Job例如 scheduleJob()、unscheduleJob()、rescheduleJob()。
5、Listener
我们有这么一种需求在每个任务运行结束之后发送通知给运维管理员。那是不是要在每个任务的最后添加一行代码呢这种方式对原来的代码造成了入侵不利于维护。如果代码不是写在任务代码的最后一行怎么知道任务执行完了呢或者说怎么监测到任务的生命周期呢
借鉴观察者模式定义对象间一种一对多的依赖关系使得每当一个对象改变状态则所有依赖它的对象都会得到通知并自动更新。
Quartz 中提供了三种 Listener监听 Scheduler 的监听 Trigger 的监听 Job 的。只需要创建类实现相应的接口并在 Scheduler 上注册 Listener便可实现对核心对象的监听。
1JobListener
使用JobListener只需要定义一个Listener实现JobListener接口并通过Listener管理器将Listener注册即可。
JobListener有四个重要方法
方法作用或执行实际getName()返回 JobListener 的名称jobToBeExecuted()Scheduler 在 JobDetail 将要被执行时调用这个方法jobExecutionVetoed()Scheduler 在 JobDetail 即将被执行但又被 TriggerListener 否决了时调用这个方法jobWasExecuted()Scheduler 在 JobDetail 被执行之后调用这个方法
// 代码实例
// 自定义Listener
public class MyJobListener implements JobListener {public String getName() {String name getClass().getSimpleName();System.out.println( Method 111111 : 获取到监听器名称name);return name;}public void jobToBeExecuted(JobExecutionContext context) {String jobName context.getJobDetail().getKey().getName();System.out.println(Method 222222 : jobName ——任务即将执行 );}public void jobExecutionVetoed(JobExecutionContext context) {String jobName context.getJobDetail().getKey().getName();System.out.println(Method 333333 : jobName ——任务被否决 );}public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {String jobName context.getJobDetail().getKey().getName();System.out.println(Method 444444 : jobName ——执行完毕 );System.out.println(------------------);}
}// 测试类
public class MyJobListenerTest {public static void main(String[] args) throws SchedulerException {// JobDetailJobDetail jobDetail JobBuilder.newJob(MyJob1.class).withIdentity(job1, group1).build();// TriggerTrigger trigger TriggerBuilder.newTrigger().withIdentity(trigger1, group1).startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();// SchedulerFactorySchedulerFactory factory new StdSchedulerFactory();// SchedulerScheduler scheduler factory.getScheduler();scheduler.scheduleJob(jobDetail, trigger);// 创建并注册一个全局的Job Listener// 工具类ListenerManager用于添加、获取、移除监听器// 工具类Matcher主要是基于 groupName 和 keyName 进行匹配。scheduler.getListenerManager().addJobListener(new MyJobListener(), EverythingMatcher.allJobs());scheduler.start();}
}2TriggerListener
方法作用或执行实际getName()返回监听器的名称triggerFired()Trigger 被触发Job 上的 execute() 方法将要被执行时Scheduler 就调用这个方法vetoJobExecution()在 Trigger 触 发 后 Job 将 要 被 执 行 时 由 Scheduler 调 用 这 个 方 法 。TriggerListener 给了一个选择去否决 Job 的执行。假如这个方法返回 true这个 Job 将不会为此次 Trigger 触发而得到执行triggerMisfired()Trigger 错过触发时调用triggerComplete()Trigger 被触发并且完成了 Job 的执行时Scheduler 调用这个方法
// 自定义TriggerListener
public class MyTriggerListener implements TriggerListener {private String name;public MyTriggerListener(String name) {this.name name;}public String getName() {return name;}// Trigger 被触发Job 上的 execute() 方法将要被执行时public void triggerFired(Trigger trigger, JobExecutionContext context) {String triggerName trigger.getKey().getName();System.out.println(Method 11111 triggerName was fired);}// 在 Trigger 触发后Job 将要被执行时由 Scheduler 调用这个方法// 返回true时这个任务不会被触发public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {String triggerName trigger.getKey().getName();System.out.println(Method 222222 triggerName was not vetoed);return false;}public void triggerMisfired(Trigger trigger) {String triggerName trigger.getKey().getName();System.out.println(Method 333333 triggerName misfired);}public void triggerComplete(Trigger trigger, JobExecutionContext context,Trigger.CompletedExecutionInstruction triggerInstructionCode) {String triggerName trigger.getKey().getName();System.out.println(Method 444444 triggerName is complete);System.out.println(------------);}
}
/*** 测试监听器*/
public class MyTriggerListenerTest {public static void main(String[] args) throws SchedulerException {// JobDetailJobDetail jobDetail JobBuilder.newJob(MyJob1.class).withIdentity(job1, group1).build();// TriggerTrigger trigger TriggerBuilder.newTrigger().withIdentity(trigger1, group1).startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever()).build();// SchedulerFactorySchedulerFactory factory new StdSchedulerFactory();// SchedulerScheduler scheduler factory.getScheduler();scheduler.scheduleJob(jobDetail, trigger);// 创建并注册一个全局的Trigger Listenerscheduler.getListenerManager().addTriggerListener(new MyTriggerListener(myListener1), EverythingMatcher.allTriggers());// 创建并注册一个局部的Trigger Listenerscheduler.getListenerManager().addTriggerListener(new MyTriggerListener(myListener2), KeyMatcher.keyEquals(TriggerKey.triggerKey(trigger1, gourp1)));// 创建并注册一个特定组的Trigger ListenerGroupMatcherTriggerKey matcher GroupMatcher.triggerGroupEquals(gourp1);scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(myListener3), matcher);scheduler.start();}}3SchedulerListener
SchedulerListener 方法比较多。
// 自定义SchedulerListener
public class MySchedulerListener implements SchedulerListener {public void jobScheduled(Trigger trigger) {String jobName trigger.getJobKey().getName();System.out.println( jobName has been scheduled);}public void jobUnscheduled(TriggerKey triggerKey) {System.out.println(triggerKey is being unscheduled);}public void triggerFinalized(Trigger trigger) {System.out.println(Trigger is finished for trigger.getJobKey().getName());}public void triggerPaused(TriggerKey triggerKey) {System.out.println(triggerKey is being paused);}public void triggersPaused(String triggerGroup) {System.out.println(trigger group triggerGroup is being paused);}public void triggerResumed(TriggerKey triggerKey) {System.out.println(triggerKey is being resumed);}public void triggersResumed(String triggerGroup) {System.out.println(trigger group triggerGroup is being resumed);}public void jobAdded(JobDetail jobDetail) {System.out.println(jobDetail.getKey() is added);}public void jobDeleted(JobKey jobKey) {System.out.println(jobKey is deleted);}public void jobPaused(JobKey jobKey) {System.out.println(jobKey is paused);}public void jobsPaused(String jobGroup) {System.out.println(job group jobGroup is paused);}public void jobResumed(JobKey jobKey) {System.out.println(jobKey is resumed);}public void jobsResumed(String jobGroup) {System.out.println(job group jobGroup is resumed);}public void schedulerError(String msg, SchedulerException cause) {System.out.println(msg cause.getUnderlyingException().getStackTrace());}public void schedulerInStandbyMode() {System.out.println(scheduler is in standby mode);}public void schedulerStarted() {System.out.println(scheduler has been started);}public void schedulerStarting() {System.out.println(scheduler is being started);}public void schedulerShutdown() {System.out.println(scheduler has been shutdown);}public void schedulerShuttingdown() {System.out.println(scheduler is being shutdown);}public void schedulingDataCleared() {System.out.println(scheduler has cleared all data);}
}/*** 测试监听器*/
public class MySchedulerListenerTest {public static void main(String[] args) throws SchedulerException {// JobDetailJobDetail jobDetail JobBuilder.newJob(MyJob1.class).withIdentity(job1, group1).build();// TriggerTrigger trigger TriggerBuilder.newTrigger().withIdentity(trigger1, group1).startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();// SchedulerFactorySchedulerFactory factory new StdSchedulerFactory();// SchedulerScheduler scheduler factory.getScheduler();scheduler.scheduleJob(jobDetail, trigger);// 创建Scheduler Listenerscheduler.getListenerManager().addSchedulerListener(new MySchedulerListener());scheduler.start();}}6、JobStore
Jobstore 用来存储任务和触发器相关的信息例如所有任务的名称、数量、状态等等。Quartz 中有两种存储任务的方式一种在在内存一种是在数据库。
在配置文件中默认配置的就是RAMJobStore
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore1RAMJobStore
Quartz 默认的 JobStore 是 RAMJobstore也就是把任务和触发器信息运行的信息存储在内存中用到了 HashMap、TreeSet、HashSet 等等数据结构。
如果程序崩溃或重启所有存储在内存中的数据都会丢失。所以我们需要把这些数据持久化到磁盘。
2JDBCJobStore
JDBCJobStore 可以通过 JDBC 接口将任务运行数据保存在数据库中。
JDBC 的实现方式有两种JobStoreSupport 类的两个子类 JobStoreTX在独立的程序中使用自己管理事务不参与外部事务。 JobStoreCMT(Container Managed Transactions (CMT)如果需要容器管理事务时使用它。
JDBCJobStore的使用
1建表。 在quartz包的src\org\quartz\impl\jdbcjobstore目录下保存着实现了JDBC的常用数据库的建表语句执行即可 2配置quartz.properties文件
# jobStore 持久化配置
#存储方式使用JobStoreTX也就是数据库
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 使用quartz.properties不使用默认配置
org.quartz.jobStore.useProperties:true
#数据库中quartz表的表名前缀
org.quartz.jobStore.tablePrefix:QRTZ_
org.quartz.jobStore.dataSource:myDS#配置数据源
org.quartz.dataSource.myDS.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL:jdbc:mysql://localhost:3306/quartz?useUnicodetruecharacterEncodingutf8
org.quartz.dataSource.myDS.user:root
org.quartz.dataSource.myDS.password:123456
org.quartz.dataSource.myDS.validationQueryselect 0 from dual表名与作用
表名作用QRTZ_BLOB_TRIGGERSTrigger 作为 Blob 类型存储QRTZ_CALENDARS存储 Quartz 的 Calendar 信息QRTZ_CRON_TRIGGERS存储 CronTrigger包括 Cron 表达式和时区信息QRTZ_FIRED_TRIGGERS存储与已触发的 Trigger 相关的状态信息以及相关 Job 的执行信息QRTZ_JOB_DETAILS存储每一个已配置的 Job 的详细信息QRTZ_LOCKS存储程序的悲观锁的信息QRTZ_PAUSED_TRIGGER_GRPS存储已暂停的 Trigger 组的信息QRTZ_SCHEDULER_STATE存储少量的有关 Scheduler 的状态信息和别的 Scheduler 实例QRTZ_SIMPLE_TRIGGERS存储 SimpleTrigger 的信息包括重复次数、间隔、以及已触的次数QRTZ_SIMPROP_TRIGGERS存储 CalendarIntervalTrigger 和 DailyTimeIntervalTrigger 两种类型的触发器QRTZ_TRIGGERS存储已配置的 Trigger 的信息
四、Quartz集成Spring
Spring 在 spring-context-support.jar 中直接提供了对 Quartz 的支持。 可以在配置文件中把 JobDetail、Trigger、Scheduler 定义成 Bean。
1、xml配置
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:contexthttp://www.springframework.org/schema/context xmlns:phttp://www.springframework.org/schema/pxmlns:aophttp://www.springframework.org/schema/aop xmlns:txhttp://www.springframework.org/schema/txxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd!--Spring整合Quartz进行配置 步骤如下1定义工作任务的Job2定义触发器Trigger并将触发器与工作任务绑定3定义调度器Scheduler并将Trigger注册到Scheduler--!-- 1定义任务的bean 这里使用JobDetailFactoryBean,也可以使用MethodInvokingJobDetailFactoryBean 配置类似--bean namemyJob1 classorg.springframework.scheduling.quartz.JobDetailFactoryBean!-- 指定job的名称 --property namename valuemy_job_1/!-- 指定job的分组 --property namegroup valuemy_group/!-- 指定具体的job类 --property namejobClass valuecom.test.quartz.MyJob1/!-- 必须设置为true如果为false当没有活动的触发器与之关联时会在调度器中会删除该任务 --property namedurability valuetrue/!-- 指定spring容器的key如果不设定在job中的jobmap中是获取不到spring容器的 --!--property nameapplicationContextJobDataKey valueapplicationContext/--/beanbean namemyJob2 classorg.springframework.scheduling.quartz.JobDetailFactoryBean!-- 指定job的名称 --property namename valuemy_job_2/!-- 指定job的分组 --property namegroup valuemy_group/!-- 指定具体的job类 --property namejobClass valuecom.test.quartz.MyJob2/!-- 必须设置为true如果为false当没有活动的触发器与之关联时会在调度器中会删除该任务 --property namedurability valuetrue/!-- 指定spring容器的key如果不设定在job中的jobmap中是获取不到spring容器的 --!--property nameapplicationContextJobDataKey valueapplicationContext/--/bean!-- 2.1定义触发器的bean定义一个Simple的Trigger一个触发器只能和一个任务进行绑定 --!-- 一个Job可以绑定多个Trigger --bean namesimpleTrigger classorg.springframework.scheduling.quartz.SimpleTriggerFactoryBean!--指定Trigger的名称--property namename valuemy_trigger_1/!--指定Trigger的组别--property namegroup valuemy_group/!--指定Tirgger绑定的Job--property namejobDetail refmyJob1/!--指定Trigger的延迟时间 1s后运行--property namestartDelay value1000/!--指定Trigger的重复间隔 5s--property namerepeatInterval value5000/!--指定Trigger的重复次数--property namerepeatCount value2//bean!-- 2.2定义触发器的bean定义一个Cron的Trigger一个触发器只能和一个任务进行绑定 --bean idcronTrigger classorg.springframework.scheduling.quartz.CronTriggerFactoryBean!-- 指定Trigger的名称 --property namename valuemy_trigger_2/!-- 指定Trigger的名称 --property namegroup valuemy_group/!-- 指定Tirgger绑定的Job --property namejobDetail refmyJob2/!-- 指定Cron 的表达式 当前是每隔1s运行一次 --property namecronExpression value0/1 * * * * ?//bean!-- 3.1 定义调度器并将Trigger注册到调度器中。这种方式任务只会存储到RAM。--bean namescheduler classorg.springframework.scheduling.quartz.SchedulerFactoryBeanproperty nametriggerslistref beansimpleTrigger/ref beancronTrigger//list/property/bean!-- 3.2 持久化数据配置需要添加quartz.properties。这种方式任务会持久化到数据库。 --
!-- bean namescheduler classorg.springframework.scheduling.quartz.SchedulerFactoryBeanproperty nameapplicationContextSchedulerContextKey valueapplicationContextKey/property nameconfigLocation valueclasspath:quartz.properties/property nametriggerslistref beansimpleTrigger/ref beancronTrigger//list/property/bean--/beanspublic class MyJob1 implements Job{private Logger log LoggerFactory.getLogger(this.getClass());public void execute(JobExecutionContext arg0) throws JobExecutionException {log.info(Job1执行 new SimpleDateFormat(yyyy-MM-dd HH:mm:ss).format(new Date()) );}
}public class MyJob2 implements Job {private Logger log LoggerFactory.getLogger(this.getClass());public void execute(JobExecutionContext arg0) throws JobExecutionException {log.info(Job2执行 new SimpleDateFormat(yyyy-MM-dd HH:mm:ss).format(new Date()) );}
}
/*** 单元测试类*/
public class QuartzTest {private static Scheduler scheduler;public static void main(String[] args) throws SchedulerException {// 获取容器ApplicationContext ac new ClassPathXmlApplicationContext(spring_quartz.xml);// 从容器中获取调度器scheduler (StdScheduler) ac.getBean(scheduler);// 启动调度器scheduler.start();}}2、注解配置
Configuration
public class QuartzConfig {Beanpublic JobDetail printTimeJobDetail(){return JobBuilder.newJob(MyJob1.class).withIdentity(testJob).usingJobData(test, hello).storeDurably().build();}Beanpublic Trigger printTimeJobTrigger() {CronScheduleBuilder cronScheduleBuilder CronScheduleBuilder.cronSchedule(0/5 * * * * ?);return TriggerBuilder.newTrigger().forJob(printTimeJobDetail()).withIdentity(quartzTaskService).withSchedule(cronScheduleBuilder).build();}
}
五、使用Quartz实现动态调度
Quartz实战基于Quartz实现定时任务的动态调度实现定时任务的增删改查
六、Quartz 集群部署
1、为什么需要集群
1、防止单点故障减少对业务的影响 2、减少节点的压力例如在 10 点要触发 1000 个任务如果有 10 个节点则每个节点之需要执行 100 个任务
2、集群需要解决的问题
1、任务重跑因为节点部署的内容是一样的到 10 点的时候每个节点都会执行相同的操作引起数据混乱。比如跑批绝对不能执行多次。 2、任务漏跑假如任务是平均分配的本来应该在某个节点上执行的任务因为节点故障一直没有得到执行。 3、水平集群需要注意时间同步问题 4、Quartz 使用的是随机的负载均衡算法不能指定节点执行
在 Quartz 中提供了一种简单的方式基于数据库共享任务执行信息。也就是说一个节点执行任务的时候会操作数据库其他的节点查询数据库便可以感知到了。使用系统自带的 11 张表即可。
3、集群配置与验证
quartz.properties 配置。 四个配置集群实例 ID、集群开关、数据库持久化、数据源信息
#如果使用集群instanceId必须唯一设置成AUTO即可
org.quartz.scheduler.instanceId AUTO
#是否使用集群
org.quartz.jobStore.isClustered true
# 数据库持久化数据源 上面分析过了此处略注意先清空 quartz 所有表、改端口、两个任务频率改成一样 验证 1先后启动 2 个节点任务是否重跑 验证 2停掉一个节点任务是否漏跑
七、Quartz 调度原理与源码分析
Quartz 调度原理与源码分析
文章转载自: http://www.morning.pxbrg.cn.gov.cn.pxbrg.cn http://www.morning.qxwgx.cn.gov.cn.qxwgx.cn http://www.morning.kybpj.cn.gov.cn.kybpj.cn http://www.morning.tslxr.cn.gov.cn.tslxr.cn http://www.morning.gkjyg.cn.gov.cn.gkjyg.cn http://www.morning.rzscb.cn.gov.cn.rzscb.cn http://www.morning.kzrbd.cn.gov.cn.kzrbd.cn http://www.morning.wcft.cn.gov.cn.wcft.cn http://www.morning.wdhzk.cn.gov.cn.wdhzk.cn http://www.morning.nfks.cn.gov.cn.nfks.cn http://www.morning.pmghz.cn.gov.cn.pmghz.cn http://www.morning.xkhxl.cn.gov.cn.xkhxl.cn http://www.morning.qtltg.cn.gov.cn.qtltg.cn http://www.morning.sgpnz.cn.gov.cn.sgpnz.cn http://www.morning.xflzm.cn.gov.cn.xflzm.cn http://www.morning.gwhjy.cn.gov.cn.gwhjy.cn http://www.morning.lokext.com.gov.cn.lokext.com http://www.morning.rfwrn.cn.gov.cn.rfwrn.cn http://www.morning.mzydm.cn.gov.cn.mzydm.cn http://www.morning.rpkl.cn.gov.cn.rpkl.cn http://www.morning.lcxdm.cn.gov.cn.lcxdm.cn http://www.morning.lpqgq.cn.gov.cn.lpqgq.cn http://www.morning.jjsxh.cn.gov.cn.jjsxh.cn http://www.morning.qmtzq.cn.gov.cn.qmtzq.cn http://www.morning.ymjgx.cn.gov.cn.ymjgx.cn http://www.morning.qwqzk.cn.gov.cn.qwqzk.cn http://www.morning.fnfhs.cn.gov.cn.fnfhs.cn http://www.morning.3jiax.cn.gov.cn.3jiax.cn http://www.morning.nfpct.cn.gov.cn.nfpct.cn http://www.morning.daidudu.com.gov.cn.daidudu.com http://www.morning.cdlewan.com.gov.cn.cdlewan.com http://www.morning.clnmf.cn.gov.cn.clnmf.cn http://www.morning.jyknk.cn.gov.cn.jyknk.cn http://www.morning.pzss.cn.gov.cn.pzss.cn http://www.morning.hhpbj.cn.gov.cn.hhpbj.cn http://www.morning.dcdhj.cn.gov.cn.dcdhj.cn http://www.morning.zrdhd.cn.gov.cn.zrdhd.cn http://www.morning.dfmjm.cn.gov.cn.dfmjm.cn http://www.morning.rsbqq.cn.gov.cn.rsbqq.cn http://www.morning.ityi666.cn.gov.cn.ityi666.cn http://www.morning.nzqmw.cn.gov.cn.nzqmw.cn http://www.morning.lhytw.cn.gov.cn.lhytw.cn http://www.morning.jsrnf.cn.gov.cn.jsrnf.cn http://www.morning.cfcdr.cn.gov.cn.cfcdr.cn http://www.morning.bbxbh.cn.gov.cn.bbxbh.cn http://www.morning.qwzpd.cn.gov.cn.qwzpd.cn http://www.morning.jjrsk.cn.gov.cn.jjrsk.cn http://www.morning.mrxgm.cn.gov.cn.mrxgm.cn http://www.morning.cgtrz.cn.gov.cn.cgtrz.cn http://www.morning.njfgl.cn.gov.cn.njfgl.cn http://www.morning.aishuxue.com.cn.gov.cn.aishuxue.com.cn http://www.morning.bmpjp.cn.gov.cn.bmpjp.cn http://www.morning.pgmyn.cn.gov.cn.pgmyn.cn http://www.morning.fhlfp.cn.gov.cn.fhlfp.cn http://www.morning.nkjjp.cn.gov.cn.nkjjp.cn http://www.morning.blqgc.cn.gov.cn.blqgc.cn http://www.morning.tddrh.cn.gov.cn.tddrh.cn http://www.morning.lmpfk.cn.gov.cn.lmpfk.cn http://www.morning.llmhq.cn.gov.cn.llmhq.cn http://www.morning.fplqh.cn.gov.cn.fplqh.cn http://www.morning.tfsyk.cn.gov.cn.tfsyk.cn http://www.morning.bpmdr.cn.gov.cn.bpmdr.cn http://www.morning.qsy38.cn.gov.cn.qsy38.cn http://www.morning.lpmlx.cn.gov.cn.lpmlx.cn http://www.morning.hlxpz.cn.gov.cn.hlxpz.cn http://www.morning.muniubangcaishui.cn.gov.cn.muniubangcaishui.cn http://www.morning.bwkzn.cn.gov.cn.bwkzn.cn http://www.morning.ntffl.cn.gov.cn.ntffl.cn http://www.morning.bqyb.cn.gov.cn.bqyb.cn http://www.morning.kfstq.cn.gov.cn.kfstq.cn http://www.morning.wbfg.cn.gov.cn.wbfg.cn http://www.morning.rdwm.cn.gov.cn.rdwm.cn http://www.morning.ffrys.cn.gov.cn.ffrys.cn http://www.morning.gnwpg.cn.gov.cn.gnwpg.cn http://www.morning.lkhfm.cn.gov.cn.lkhfm.cn http://www.morning.hxljc.cn.gov.cn.hxljc.cn http://www.morning.cprls.cn.gov.cn.cprls.cn http://www.morning.tlbdy.cn.gov.cn.tlbdy.cn http://www.morning.fbdkb.cn.gov.cn.fbdkb.cn http://www.morning.pljdy.cn.gov.cn.pljdy.cn