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 ...
随机推荐
- 洛谷P4343 [SHOI2015]自动刷题机
题目 易得该题目中的\(n\)和\(k\)具有单调性,满足二分的性质,因此该题目而已用二分来枚举\(n\),然后对于每个\(n\)模拟出它所对应的\(k\),然后注意注意代码细节,并且当当前\(k\) ...
- vuex基础入门
Vuex简介 vuex的安装和组成介绍 [外链图片转存失败(img-nWQUUuyh-1565273314232)(https://upload-images.jianshu.io/upload_im ...
- 第09组 团队Git现场编程实战
组长博客链接 1.团队分工 团队成员 分工明细 王耀鑫 博客撰写,数据处理 陈志荣 前端界面,前端功能实现 陈超颖 前端界面,前端功能实现 沈梓耀 前端界面,前端功能实现 林明镇 数据处理 滕佳 前端 ...
- Java使用Jsoup之爬取博客数据应用实例
导入Maven依赖 <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup --> <dependency> <g ...
- 安装关系型数据库MySQL 安装大数据处理框架Hadoop
作业要求来自:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3161 1.Hadoop的介绍 Hadoop最早起源于Nutch.Nut ...
- JAVA学习网站分享
好久没用博客园了 最近工作不忙了,可以花时间自己学习知识提升自己,所以开始查找各种资料 java资源查找网站: http://www.java1234.com/ 前端学习网站: ...
- [转]Java 之 Serializable 序列化和反序列化的概念,作用的通俗易懂的解释
原文地址:https://blog.csdn.net/qq_27093465/article/details/78544505 遇到这个 Java Serializable 序列化这个接口,我们可能会 ...
- web服务器请求代理方式
1 通信数据转发程序:代理.网关.隧道 代理:是一种有转发功能的应用程序,他扮演了位于服务器和客户端“中间人”的角色,接收客户端发送的请求并转发给服务器:同时也接收服务器返回的响应并转发给客户端. 使 ...
- EasyDSS高性能RTMP/FLV/HLS(m3u8)/RTSP流媒体服务器技术的HTTP QueryString URL的C++实现方案
EasyDSS支持HTTP GET接口访问,我们需要获取url的各种参数信息 比如 http://ip:port/action?a=1&b=2&c=3 我们需要知道对应的a.b.c的值 ...
- EasyDSS RTMP流媒体服务器基于RTMP拉模式和转推模式搭建公网全平台全终端直播集群的方案
近期,我们有一位EasyDSS流媒体用户在需求中提出:如何搭建一个用户量巨大的讲座直播,提供给普通用户免费观看,每天上午和下午分别有几场讲座同时直播,持续几天时间. 推荐方案 ★ 由于甲方是中国移动运 ...