四. Hello JDBC Quartz!

  1. JDBC方式: 就是说通过数据库的jdbc链接来进行quartz的一个配置 Quartz支持了很好的支持

    demo用例 使用mysql作为例子进行演示

    相比简单配置多出了 :

    • 数据库

    • 数据库结构 (需要我们手动去初始化一些表格)

    • 配置 quartz.properties

  2. 实际上是否使用jdbc模式的quartz 完全取决于 业务 , 当定时任务比较多的时候, 可以选择使用jdbc方式

    简单来看这种方式的优点就是我们可以进行一些简单的改造就能达到 动态控制定时任务的效果,缺点就是 他的性能远不如ram方式 毕竟他是建立在jdbc链接上也部分依赖于网络速度

  3. make it :

    1. mysql 安装

    2. 导入 初始化数据库.sql文件 的下载

      我在官网文档中查看 发现并没有写在文档中

      在下载的quartz压缩包中的 .\docs\dbTables 有各种数据库的初始话的.sql文件

      压缩包我上传了git: (https://github.com/wunian7yulian/GITHUB_WORKSPACE) 2.23 版本

    3. 选择 tables_db2_v8.sql 进行导入  

      1. 为了防止重复 都加了 qrtz作为表名的前缀

      2. 表实际上在 第一章 5. 中有过相应的介绍

    4. 代码编写

      1. 第一章 7. 中 介绍了quartz相关的可以更改的配置(quartz.properties ):

        //调度标识名 集群中每一个实例都必须使用相同的名称 (区分特定的调度器实例)
        org.quartz.scheduler.instanceName:DefaultQuartzScheduler
        //ID设置为自动获取 每一个必须不同 (所有调度器实例中是唯一的)
        org.quartz.scheduler.instanceId :AUTO
        //数据保存方式为持久化
        org.quartz.jobStore.class :org.quartz.impl.jdbcjobstore.JobStoreTX
        //表的前缀
        org.quartz.jobStore.tablePrefix : QRTZ_
        //设置为TRUE不会出现序列化非字符串类到 BLOB 时产生的类版本问题
        //org.quartz.jobStore.useProperties : true
        //加入集群 true 为集群 false不是集群
        org.quartz.jobStore.isClustered : false
        //调度实例失效的检查时间间隔
        org.quartz.jobStore.clusterCheckinInterval:20000
        //容许的最大作业延长时间
        org.quartz.jobStore.misfireThreshold :60000
        //ThreadPool 实现的类名
        org.quartz.threadPool.class:org.quartz.simpl.SimpleThreadPool
        //线程数量
        org.quartz.threadPool.threadCount : 10
        //线程优先级
        org.quartz.threadPool.threadPriority : 5(threadPriority 属性的最大值是常量 java.lang.Thread.MAX_PRIORITY,等于10。最小值为常量 java.lang.Thread.MIN_PRIORITY,为1)
        //自创建父线程
        //org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
        //数据库别名
        org.quartz.jobStore.dataSource : qzDS
        //设置数据源
        org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver
        org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartz
        org.quartz.dataSource.qzDS.user:root
        org.quartz.dataSource.qzDS.password:123456
        org.quartz.dataSource.qzDS.maxConnection:10

        使用jdbc方式需要配置数据源:

        #my datasource 配置自己的数据库链接
        org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver
        org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartz_db
        org.quartz.dataSource.qzDS.user:root
        org.quartz.dataSource.qzDS.password:123456
        org.quartz.dataSource.qzDS.maxConnection:10
      1. 先创建job

        package com.ws.quartzdemo1001.job02_JDBC_HelloWorld;

        import com.ws.quartzdemo1001.job01_HelloWorld.HelloJob;
        import org.quartz.Job;
        import org.quartz.JobExecutionContext;
        import org.quartz.JobExecutionException;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        import java.text.SimpleDateFormat;
        import java.util.Date;

        public class MyJobForJDBCQuartz implements Job {

        private static Logger log = LoggerFactory.getLogger(MyJobForJDBCQuartz.class);

        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
        log.info("MyJobForJDBCQuartz is start ..................");

        log.info("Hello JDBC Quartz !!! "+
        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ").format(new Date()));

        log.info("MyJobForJDBCQuartz is end .....................");
        }
        }
      2. 编写调度程序

        package com.ws.quartzdemo1001.job02_JDBC_HelloWorld;

        import org.quartz.*;
        import org.quartz.impl.StdSchedulerFactory;

        public class QuartzJDBCTest {
        public static void main(String[] args) throws SchedulerException {
        // 1 创建 一个jobDetail 实例
        JobDetail jobDetail = JobBuilder.newJob(MyJobForJDBCQuartz.class)
        .withIdentity("jdbcJob_01","jdbcGroup_01")
        .storeDurably(true)
        .build();
        // 2 创建 简单的调度器
        SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder
        //设置执行次数
        .repeatSecondlyForTotalCount(5);
        // 3 创建 触发器 Trigger
        Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("jdbcTrigger_01","jdbcTriggerGroup_01")
        .startNow().withSchedule(simpleScheduleBuilder).build();
        // 4 获取 调度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
        // 5 执行 相关调度
        scheduler.scheduleJob(jobDetail,trigger);
        // 6 关闭 调度器
        scheduler.shutdown();
        }
        }

        运行时发现启动不起来 原来忘了没有数据库的一个驱动 jar 做path 引入:

                <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.35</version>
        </dependency>

        然后启动 发现 正确执行并打印了5次

        其中JobDetail在创建时 : storeDurably(true) 标识任务将会记录在数据库中保存起来

        当下次执行时不需要重复创建jobDetail

        尝试多次执行: 抛出:

        Exception in thread "main" org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'jdbcGroup_01.jdbcJob_01', because one already exists with this identification.
        at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1108)
        at org.quartz.impl.jdbcjobstore.JobStoreSupport$2.executeVoid(JobStoreSupport.java:1062)
        at org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3703)
        at org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3701)
        at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3787)
        at org.quartz.impl.jdbcjobstore.JobStoreTX.executeInLock(JobStoreTX.java:93)
        at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJobAndTrigger(JobStoreSupport.java:1058)
        at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:886)
        at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:249)
        at com.ws.quartzdemo1001.job02_JDBC_HelloWorld.QuartzJDBCTest.main(QuartzJDBCTest.java:25)
      3. 当我回去看的时候发现日志里面打印了类如:

        14:29:35.085 [QuartzScheduler_DefaultQuartzScheduler-NON_CLUSTERED_MisfireHandler] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - MisfireHandler: scanning for misfires...
        14:29:35.086 [QuartzScheduler_DefaultQuartzScheduler-NON_CLUSTERED_MisfireHandler] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - Found 0 triggers that missed their scheduled fire-time.
        14:29:55.106 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 0 triggers

        这里其实是一个任务进度的扫描 misfires 也是quartz 的一个独有 且完善的一个机制 他保证了我们的所有该执行的任务不会丢失掉

        由此看来 quartz的 是一个高度可用 有着非常完美的适用方案的一个调度框架

五.Demo 其他了解

作为简单demo 我们需要充分了解该demo 适用工作中的业务场景

如:

  • 定时发邮件场景

  • 耗时任务场景 - 并发

  • 集群场景

  • 其他需要注意

  1. 定时发邮件场景

    因为我们定时任务的实现类 再编写的时候 只是去实现Job接口 中的 execute() 接口

      public void execute(JobExecutionContext context) throws JobExecutionException

    而再发邮件业务中 我们需要再任务执行的时候知道 接收方邮件的地址

    再execute 的 形参中 我们无法进行自定义处理

    当然,我们可以 设置成 再execute 里面查出列表 一个for 循环搞定

    但是我们可以考虑一下 如何进行传参 将邮件地址传入进来

    JobDataMap quartz 提供了一个map来方便我们业务的书写

    • 设置值: (在jobdetail 创建的时候)

    job.usingJobData("age", 18) //方法一  加入age属性到JobDataMap
    job.getJobDataMap().put("name", "quertz"); //方法二 加入属性name到JobDataMap
    • 取值: (在execute 中)

      JobDetail detail = context.getJobDetail();
    JobDataMap map = detail.getJobDataMap(); //方法一:获得JobDataMap 然后进行取值

    或者

     private int age;
    public void setAge(String age) { //方法二: 声明对应属性的setter 方法 会自动注入
    this.age = age;
    }

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

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

  2. 耗时任务场景

    Job是有可能并发执行的,比如一个任务要执行10秒中,而调度算法是每秒中触发1次,那么就有可能多个任务被并发执行。

    有时候我们并不想任务并发执行,比如这个任务要去”获得数据库中未进行发放红包的用户“,如果是并发执行,就需要一个数据库锁去避免一个数据被多次处理。这个时候一个@DisallowConcurrentExecution解决这个问题。

    @DisallowConcurrentExecution
    public class DoNothingJob implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
    System.out.println("do nothing");
    }
    }

    注意,@DisallowConcurrentExecution是对JobDetail实例生效,也就是如果你定义两个JobDetail,引用同一个Job类,是可以并发执行的。

  3. 集群场景

    http://www.cnblogs.com/zhenyuyaodidiao/p/4755649.html 参考这位大神文章 如果有用到的机会 看一下

    其实各种场景和demo 官方都有提供

    集群的的介绍也是在Terracotta收购后更加完善了

  4. 其他注意点 :(持续补充)

    JobExecutionException

    Job.execute()方法是不允许抛出除JobExecutionException之外的所有异常的(包括RuntimeException),所以编码的时候,最好是try-catch住所有的Throwable,小心处理。

    Durability(持久)

    如果一个任务不是durable,那么当没有Trigger关联它的时候,它就会被自动删除。

    RequestsRecovery

    如果一个任务是"requests recovery",那么当任务运行过程非正常退出时(比如进程崩溃,机器断电,但不包括抛出异常这种情况),Quartz再次启动时,会重新运行一次这个任务实例。

    可以通过JobExecutionContext.isRecovering()查询任务是否是被恢复的

Quartz学习--三 Hello Jdbc Quartz! 和 demo 结尾的更多相关文章

  1. Quartz学习笔记1:Quartz概述

    Quartz是开源任务调度框架中的翘楚,它提供了强大的 任务调度机制.Quartz允许开发人员灵活的定义触发器的调度时间表,并可对触发器和任务进行关联映射.此外,Quartz提供了调度运行环境的持久化 ...

  2. Quartz学习——Spring和Quartz集成详解(三)

    Spring是一个很优秀的框架,它无缝的集成了Quartz,简单方便的让企业级应用更好的使用Quartz进行任务的调度.下面就对Spring集成Quartz进行简单的介绍和示例讲解!和上一节 Quar ...

  3. Java任务调度开源框架quartz学习

    一.quartz学习 Java框架介绍:Quartz从入门到进阶 http://edu.yesky.com/edupxpt/233/2209233.shtml 1.例子:http://javacraz ...

  4. Quartz学习——Quartz简单入门Demo(二)

    要学习Quartz框架,首先大概了解了Quartz的基本知识后,在通过简单的例子入门,一步一个脚印的走下去. 下面介绍Quartz入门的示例,由于Quartz的存储方式分为RAM和JDBC,分别对这两 ...

  5. Quartz学习--二 Hello Quartz! 和源码分析

    Quartz学习--二  Hello Quartz! 和源码分析 三.  Hello Quartz! 我会跟着 第一章 6.2 的图来 进行同步代码编写 简单入门示例: 创建一个新的java普通工程 ...

  6. Quartz学习——SSMM(Spring+SpringMVC+Mybatis+Mysql)和Quartz集成详解(转)

    通过前面的学习,你可能大致了解了Quartz,本篇博文为你打开学习SSMM+Quartz的旅程!欢迎上车,开始美好的旅程! 本篇是在SSM框架基础上进行的. 参考文章: 1.Quartz学习——Qua ...

  7. Quartz学习——SSMM(Spring+SpringMVC+Mybatis+Mysql)和Quartz集成详解(四)

    当任何时候觉你得难受了,其实你的大脑是在进化,当任何时候你觉得轻松,其实都在使用以前的坏习惯. 通过前面的学习,你可能大致了解了Quartz,本篇博文为你打开学习SSMM+Quartz的旅程!欢迎上车 ...

  8. Quartz学习-- quartz基本介绍和 Cron表达式

    Quartz学习 一. Quartz 大致简介 Quartz 是完全由 java 开发的一个开源的任务日程管理系统 ​任务日程管理系统 换句话说就是: ​ 一个预先确定的日程时间到达时,负责执行任务的 ...

  9. (转) Quartz学习——SSMM(Spring+SpringMVC+Mybatis+Mysql)和Quartz集成详解(四)

    http://blog.csdn.net/u010648555/article/details/60767633 当任何时候觉你得难受了,其实你的大脑是在进化,当任何时候你觉得轻松,其实都在使用以前的 ...

随机推荐

  1. 软工之词频统计器及基于sketch在大数据下的词频统计设计

    目录 摘要 算法关键 红黑树 稳定排序 代码框架 .h文件: .cpp文件 频率统计器的实现 接口设计与实现 接口设计 核心功能词频统计器流程 效果 单元测试 性能分析 性能分析图 问题发现 解决方案 ...

  2. EF Core 2.0中如何手动映射数据库的视图为实体

    由于Scaffold-DbContext指令目前还不支持自动映射数据库中的视图为实体,所以当我们想使用EF Core来读取数据库视图数据的时候,我们需要手动去做映射,本文介绍如何在EF Core中手动 ...

  3. oAuth2.0认证流程图

    这两天在看oAuth2.0的东西,简单的使用visio画了个流程图.演示的是用户登录慕课网,使用qq登录的流程:

  4. kvo本质探寻

    一.概述 1.本文章内容,须参照本人的另一篇博客文章“class和object_getClass方法区别”加以理解: 2.基本使用: //给实例对象instance添加观察者,监听该实例对象的某个属性 ...

  5. Mysql 查询是否锁表

    1.查询是否锁表show OPEN TABLES where In_use > 0; 2.查询进程 show processlist 查询到相对应的进程===然后 kill id 补充:查看正在 ...

  6. 对类Vue的MVVM前端库的实现

    关于实现MVVM,网上实在是太多了,本文为个人总结,结合源码以及一些别人的实现 关于双向绑定 vue 数据劫持 + 订阅 - 发布 ng 脏值检查 backbone.js 订阅-发布(这个没有使用过, ...

  7. x01.os.24: 来点代码

    <Orange'S 一个操作系统的实现>源代码 <Linux 0.11 内核完全注释>源代码 linux-0.12 源代码:  解决了 Not Owner 问题 闲来无事,在 ...

  8. python爬虫同时输出两个列表(zip函数)

    简介:在做爬虫时,xpath返回的是列表格式,我们又需要将列表中的元素一一对应并存放至字典中,这是就可以用zip函数. zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组, ...

  9. LIFO栈 ADT接口 数组实现

    LIFO 栈结构 typedef int ElemenType; struct seqStack{ ElemeType data[MaxSize]; int top; }; typedef struc ...

  10. Golang 对接宝付、通联、富友金账户...填坑记

    一.宝付私钥加密,公钥解密 由于对RSA加密解密原理不是很熟悉,宝付也没有Golang的Demo提供.Go语言库里一般都是私钥解密.公钥加密,或者私钥签名.公钥验签.宝付需要反过来,这里也到好找到了h ...