quartz2.3.0(十三)数据库持久化定时器job任务和trigger触发器,在多个调度器实例情况下,由其它调度器实例恢复执行调度器宕机的job任务
一、初始化数据库11张quartz表:qrtz_*
先从官网下载好quartz2.3.0包:http://www.quartz-scheduler.org/downloads/
解压后进入目录:quartz-2.3.0-SNAPSHOT\src\org\quartz\impl\jdbcjobstore
得到22种数据库的11张qrtz_*表的初始化SQL,这里列举几个经典的数据库文件:tables_oracle.sql、tables_mysql_innodb.sql、tables_sqlServer.sql、tables_postgres.sql

二、配置定时器quartz.properties
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName=TestScheduler
#instance_1 更改后缀数字后,程序可以执行不同ID的调度器,启动多个调度器,有利于观察其中一个实例宕机后,另外实例恢复这个实例的job任务情况
org.quartz.scheduler.instanceId=instance_1 #============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=5
org.quartz.threadPool.threadPriority=5 #============================================================================
# 配置Oracle数据库,命名dataSource为myDS
#============================================================================
# 支持PostgreSQL数据库
#org.quartz.dataSource.myDS.driver=org.postgresql.Driver
#org.quartz.dataSource.myDS.URL=jdbc:postgresql://localhost:5432/quartz
org.quartz.dataSource.myDS.driver=oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.myDS.URL=jdbc:oracle:thin:@localhost:1521:orcl
org.quartz.dataSource.myDS.user=zhuwen
org.quartz.dataSource.myDS.password=ZHUwen12
org.quartz.dataSource.myDS.maxConnections=5
org.quartz.dataSource.myDS.validationQuery=select 0 FROM DUAL #============================================================================
# 配置job任务存储策略,指定一个叫myDS的dataSource
#============================================================================
org.quartz.jobStore.misfireThreshold: 60000
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
# 支持PostgreSQL数据库
#org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
# 支持Oracle数据库
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.dataSource=myDS
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
三、job任务类
第一个job类:SimpleRecoveryJob
package org.quartz.examples.example13; import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.util.Date; /**
* 一个愚蠢的作业实现,用于单元测试。
*/
public class SimpleRecoveryJob implements Job { private static Logger LOG = LoggerFactory.getLogger(SimpleRecoveryJob.class); private static final String COUNT = "count"; //必须要有public修饰的无参构造函数
public SimpleRecoveryJob() {
} //任务执行方法
public void execute(JobExecutionContext context) throws JobExecutionException { JobKey jobKey = context.getJobDetail().getKey(); // 如果作业正在恢复。如果由于“恢复”情况而重新执行作业,此方法将返回true。
if (context.isRecovering()) {
// LOG.info("恢复作业:SimpleRecoveryJob: " + jobKey + " RECOVERING at " + new Date());
System.err.println("恢复作业:SimpleRecoveryJob: " + jobKey + " RECOVERING at " + new Date());
} else {
// LOG.info("不恢复作业:SimpleRecoveryJob: " + jobKey + " starting at " + new Date());
System.err.println("不恢复作业:SimpleRecoveryJob: " + jobKey + " starting at " + new Date());
} // 睡眠10秒
long delay = 10L * 1000L;
try {
Thread.sleep(delay);
} catch (Exception e) {
//
} JobDataMap data = context.getJobDetail().getJobDataMap();
int count;
if (data.containsKey(COUNT)) {
count = data.getInt(COUNT);
} else {
count = 0;
}
count++;
data.put(COUNT, count); LOG.info("SimpleRecoveryJob: " + jobKey + " done at " + new Date() + "\n Execution #" + count); } }
下面是第二个job类,注意这个job类不允许多线程并发执行:
package org.quartz.examples.example13; import org.quartz.DisallowConcurrentExecution;
import org.quartz.PersistJobDataAfterExecution; /**
* 这个作业具有与SimpleRecoveryJob相同的功能,只是这个作业实现是“有状态的”,
* 因为它将在每次执行之后自动重新持久化它的数据(JobDataMap),并且一次只能执行JobDetail的一个实例。
*
*/
@PersistJobDataAfterExecution //持久化JobDataMap里的数据,使下一个定时任务还能获取到这些值
@DisallowConcurrentExecution //禁止并发多任务执行,所以永远只有一个任务在执行中
public class SimpleRecoveryStatefulJob extends SimpleRecoveryJob { public SimpleRecoveryStatefulJob() {
super();
}
}
四、任务调度类
package org.quartz.examples.example13; import static org.quartz.DateBuilder.futureDate;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger; import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* 用于测试/显示JDBCJobStore (JobStoreTX或JobStoreCMT)的集群特性。
* <p>
* 所有实例必须使用不同的属性文件,因为它们的实例id必须不同,但是所有其他属性应该相同。
* </p>
* <p>
* 如果您希望它清除现有的作业&触发器,设置clearJobs=true
* </p>
* <p>
* 您可能需要先清空残留的表数据,因为将来自非集群设置的数据与集群设置的数据混合在一起可能是不好的。
* </p>
* <p>
* 你可以在运行多个Scheduler实例的时候,杀死其中一个实例,并确保其余实例恢复正在进行的作业。注意,使用默认设置检测故障可能需要15秒左右的时间。
* </p>
* <p>
* 还可以尝试使用/不使用调度程序注册的shutdown-hook插件运行它。(org.quartz.plugins.management.ShutdownHookPlugin)。
* </p>
* <p>
* 注意:从不要在单独的机器上运行集群,除非它们的时钟使用某种形式的时间同步服务(例如NTP守护进程)进行同步。
* </p>
*/
public class ClusterExample { private static Logger LOG = LoggerFactory.getLogger(ClusterExample.class); public void run(boolean inClearJobs, boolean inScheduleJobs) throws Exception { // 初始化调度器
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler(); if (inClearJobs) {
LOG.warn("***** 删除已存在的job任务和triggers触发器。Deleting existing jobs/triggers *****");
sched.clear();
} if (inScheduleJobs) {
LOG.info("------- Scheduling Jobs ------------------");
String schedId = sched.getSchedulerInstanceId(); // ========================================================
// ============ job1
// ========================================================
int count = 1;
JobDetail job = newJob(SimpleRecoveryJob.class).withIdentity("job_" + count, schedId).requestRecovery() // 如果job执行过程中宕机,则job重新执行
.build();
SimpleTrigger trigger = newTrigger().withIdentity("triger_" + count, schedId)
.startAt(futureDate(1, IntervalUnit.SECOND))
.withSchedule(simpleSchedule().withRepeatCount(20).withIntervalInSeconds(5)).build();
LOG.info(job.getKey() + " will run at: " + trigger.getNextFireTime() + " and repeat: "
+ trigger.getRepeatCount() + " times, every " + trigger.getRepeatInterval() / 1000 + " seconds");
sched.scheduleJob(job, trigger); // ========================================================
// ============ job2
// ========================================================
count++;
job = newJob(SimpleRecoveryJob.class).withIdentity("job_" + count, schedId).requestRecovery() // 如果job执行过程中宕机,则job重新执行
.build();
trigger = newTrigger().withIdentity("triger_" + count, schedId).startAt(futureDate(2, IntervalUnit.SECOND))
.withSchedule(simpleSchedule().withRepeatCount(20).withIntervalInSeconds(5)).build();
LOG.info(job.getKey() + " will run at: " + trigger.getNextFireTime() + " and repeat: "
+ trigger.getRepeatCount() + " times, every " + trigger.getRepeatInterval() / 1000 + " seconds");
sched.scheduleJob(job, trigger); // ========================================================
// ============ job3,这里使用的是job子类SimpleRecoveryStatefulJob
// ========================================================
count++;
job = newJob(SimpleRecoveryStatefulJob.class).withIdentity("job_" + count, schedId).requestRecovery() // 如果job执行过程中宕机,则job重新执行
.build();
trigger = newTrigger().withIdentity("triger_" + count, schedId).startAt(futureDate(1, IntervalUnit.SECOND))
.withSchedule(simpleSchedule().withRepeatCount(20).withIntervalInSeconds(3)).build();
LOG.info(job.getKey() + " will run at: " + trigger.getNextFireTime() + " and repeat: "
+ trigger.getRepeatCount() + " times, every " + trigger.getRepeatInterval() / 1000 + " seconds");
sched.scheduleJob(job, trigger); // ========================================================
// ============ job4
// ========================================================
count++;
job = newJob(SimpleRecoveryJob.class).withIdentity("job_" + count, schedId).requestRecovery() // 如果job执行过程中宕机,则job重新执行
.build();
trigger = newTrigger().withIdentity("triger_" + count, schedId).startAt(futureDate(1, IntervalUnit.SECOND))
.withSchedule(simpleSchedule().withRepeatCount(20).withIntervalInSeconds(4)).build();
LOG.info(job.getKey() + " will run at: " + trigger.getNextFireTime() + " & repeat: "
+ trigger.getRepeatCount() + "/" + trigger.getRepeatInterval());
sched.scheduleJob(job, trigger); // ========================================================
// ============ job5
// ========================================================
count++;
job = newJob(SimpleRecoveryJob.class).withIdentity("job_" + count, schedId).requestRecovery() // 如果job执行过程中宕机,则job重新执行
.build();
trigger = newTrigger().withIdentity("triger_" + count, schedId).startAt(futureDate(1, IntervalUnit.SECOND))
.withSchedule(simpleSchedule().withRepeatCount(20).withIntervalInMilliseconds(4500L)).build();
LOG.info(job.getKey() + " will run at: " + trigger.getNextFireTime() + " & repeat: "
+ trigger.getRepeatCount() + "/" + trigger.getRepeatInterval());
sched.scheduleJob(job, trigger);
} sched.start(); LOG.info("------- Waiting for one hour... ----------");
try {
Thread.sleep(3600L * 1000L);
} catch (Exception e) {
//
} sched.shutdown();
LOG.info("------- Shutdown Complete ----------------");
} public static void main(String[] args) throws Exception {
boolean clearJobs = false;
boolean scheduleJobs = true; for (String arg : args) {
if (arg.equalsIgnoreCase("clearJobs")) {
clearJobs = true;
} else if (arg.equalsIgnoreCase("dontScheduleJobs")) {
scheduleJobs = false;
}
} clearJobs = true;
scheduleJobs = true; ClusterExample example = new ClusterExample();
example.run(clearJobs, scheduleJobs);
}
}
quartz2.3.0(十三)数据库持久化定时器job任务和trigger触发器,在多个调度器实例情况下,由其它调度器实例恢复执行调度器宕机的job任务的更多相关文章
- kafka producer 打数据,ack 为 0, 1, -1 的时候代表啥, 设置 -1 的时候,什么情况下,leader 会认为一条消息 commit了?
1(默认) 数据发送到Kafka后,经过leader成功接收消息的的确认,就算是发送成功了.在这种情况下,如果leader宕机了,则会丢失数据. 0 生产者将数据发送出去就不管了,不去等待任何返回. ...
- quartz2.3.0系列目录——带您由浅入深全面掌握quartz2.3.0
quartz2.3.0系列目录 官网下载地址:http://www.quartz-scheduler.org/downloads/ 本系列demo全部来源于官网,仅仅是简化和汉化了注释!一部分代码de ...
- quartz2.3.0(十五)执行、暂停、继续执行、清除,花式操作数据库中持久化的job任务
#################################################################################################### ...
- Kafka 0.8 宕机问题排查步骤
CPU 利用率高的排查方法 看看该机器的连接数是不是比其他机器多,监听的端口数:netstat -anlp | wc -l Kafka-0.8的停止和启动 启动: cd /usr/local/kafk ...
- android数据库持久化框架, ormlite框架,
前言 Android中内置了SQLite,但是对于数据库操作这块,非常的麻烦.其实可以试用第3方的数据库持久化框架对之进行结构上调整, 摆脱了访问数据库操作的细节,不用再去写复杂的SQL语句.虽然这样 ...
- Spring+Redis集成+关系型数据库持久化
本篇文章主要介绍了"Spring+Redis集成+关系型数据库持久化",主要涉及到Spring+Redis集成+关系型数据库持久化方面的内容,对于Spring+Redis集成+关系 ...
- quartz2.3.0(十)xml配置方式定义quartz定时任务
1.新增pom依赖 除了按照<quartz2.3.0系列目录——带您由浅入深全面掌握quartz2.3.0>添加依赖之外,pom.xml里新增加依赖: <dependency> ...
- SpringCloud微服务实战——搭建企业级开发框架(五):数据库持久化集成MySql+Druid+MyBatis-Plus
在引入相关数据库持久化相关依赖库之前,我们可以考虑到,当我们因业务开发需要,引入各种各样的依赖库时,Jar包冲突是我们必须面对的一个问题,Spring为了解决这些Jar包的冲突,推出了各种bom, ...
- 删库到跑路?还得看这篇Redis数据库持久化与企业容灾备份恢复实战指南
本章目录 0x00 数据持久化 1.RDB 方式 2.AOF 方式 如何抉择 RDB OR AOF? 0x01 备份容灾 一.备份 1.手动备份redis数据库 2.迁移Redis指定db-数据库 3 ...
随机推荐
- CSPS_110
永远不要相信出题人诸如“保证图联通”之类的鬼话. T1 最优情况一定为从LR最高的不同位以下全是1 T2 折半搜索 T3 1.我算法不是mlog^2m,最坏情况下mlogm再乘个根号m, 考试的时候没 ...
- 洛谷 P2004 领地选择 题解
P2004 领地选择 题目描述 作为在虚拟世界里统帅千军万马的领袖,小Z认为天时.地利.人和三者是缺一不可的,所以,谨慎地选择首都的位置对于小T来说是非常重要的. 首都被认为是一个占地C*C的正方形. ...
- 网络开发Socket和ServerSocket
已经发表个人公众号 Socket和ServerSocket Socket为"孔"或"插座",创建Socket,打开连接Socket的输入或输出流,对Socket ...
- C语言函数内局部变量释放的坑
首先把代码贴上来: #include <stdio.h> #include<windows.h> int f(int **iptr){ ; *iptr = &a; ; ...
- 新版本Mariadb安装后相关问题的解决
给新机器Ubuntu安装的Mariadb后无法登录,通过网上各种方法修改root用户密码,仍然无法解决,耗费几个小时! 经过看日志和查手册,发现原因如下: ubuntu确实安装没有启用root用户,所 ...
- Apache Flink - 架构和拓扑
Flink结构: flink cli 解析本地环境配置,启动 ApplicationMaster 在 ApplicationMaster 中启动 JobManager 在 ApplicationMas ...
- socket http tcp ip 区别联系
功能是实现继承复用.刚才做了一个简要的概述,里面有一些常用的概念,这里做个简短的概念普及介绍:(1),TCP/IP------TPC/IP协议是传输层协议,主要解决数据如何在网络中传输.(2),Soc ...
- 刷题记录:[ByteCTF 2019]EZCMS
目录 刷题记录:[ByteCTF 2019]EZCMS 一.知识点 1.源码泄露 2.MD5长度扩展攻击 3.php://filter绕过正则实现phar反序列化 刷题记录:[ByteCTF 2019 ...
- 【大数据作业十】分布式文件系统HDFS 练习
作业要求来自: https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3292 利用Shell命令与HDFS进行交互 以”./bin/dfs ...
- free中buffer 与 cache 的区别
通常人们所说的Cache就是指缓存SRAM. SRAM叫静态内存,“静态”指的是当我们将一笔数据写入SRAM后,除非重新写入新数据或关闭电源,否则写入的数据保持不变. 由于CPU的速度比内存和硬盘的速 ...