quartz学习四--存储、集群

原创 2016年06月01日 15:31:29
一、Quartz的存储和持久化
    1、存储机制
       Quartz 用 JobStores 对 Job、Trigger、calendar 和 Schduler 数据提供一种存储机制。

       Scheduler 应用已配置的JobStore 来存储和获取到部署信息,并决定正被触发执行的 Job 的职责。

       所有的关于哪个 Job 要执行和以什么时间表来执行他们的信息都来存储在 JobStore。

    2、存储类型
       内存(非持久化) 存储

       持久化存储

    3、JobStore 接口
       (1)、作用
            所有的Job存储机制,不管是在哪里或是如何存储他们的信息的,都必须实现这个接口。

       (2)、JobStore 接口的 API 分类
            Job 相关的 API

            Trigger 相关的 API

            Calendar 相关的 API

            Scheduler 相关的 API

    4、内存(非持久化) JobStore
       (1)、org.quartz.simple.RAMJobStore
            a、作用
           使用内存来存储 Scheduler 信息 , 是 Quartz 的默认的解决方案。

            b、优势
           RAMJobStore是配置最简单的 JobStore:默认已经配置好了。见 quartz.jar:org.quartz.quartz.properties

           RAMJobStore的速度非常快。所有的 quartz存储操作都在计算机内存中

    5、持久化 JobStore
       (1)、组成
            持久化 JobStore = JDBC + 关系型数据库

       (2)、org.quartz.impl.jdbcjobstore.JobStoreSupport 类
            Quartz 所有的持久化的 JobStore 都扩展自 JobStoreSupport 类。

       (3)、JobStoreSupport实现类
            a、每一个设计为针对特定的数据库环境和配置

            b、org.quartz.impl.jdbcjobstore.JobStoreTX
           类设计为用于独立环境中。这里的 "独立",我们是指这样一个环境,在其中不存在与应用容器的事务集成。

            c、org.quartz.impl.jdbcjobstore.JobStoreCMT
           类设计为与程序容器事务集成,容器管理的事务(Container Managed Transactions (CMT))

            d、JobStore 配置
           #properties配置
           org.quartz.jobStore.class = org.quartz.ompl.jdbcjobstore.JobStoreTX

       (4)、数据库结构 -- 表名描述
            QRTZ_CALENDARS                    --->        以 Blob 类型存储 Quartz 的 Calendar 信息
            QRTZ_CRON_TRIGGERS                    --->        存储 Cron Trigger,包括 Cron 表达式和时区信息
            QRTZ_FIRED_TRIGGERS                    --->        存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息
            QRTZ_PAUSED_TRIGGER_GRPS                --->        存储已暂停的 Trigger 组的信息
            QRTZ_SCHEDULER_STATE                --->        存储少量的有关 Scheduler 的状态信息,和别的 Scheduler 实例(假如是用于一个集群中)
            QRTZ_LOCKS                        --->        存储程序的悲观锁的信息(假如使用了悲观锁)
            QRTZ_JOB_DETAILS                    --->        存储每一个已配置的 Job 的详细信息
            QRTZ_JOB_LISTENERS                    --->        存储有关已配置的 JobListener 的信息
            QRTZ_SIMPLE_TRIGGERS                --->        存储简单的 Trigger,包括重复次数,间隔,以及已触的次数
            QRTZ_BLOG_TRIGGERS                    --->        Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)
            QRTZ_TRIGGER_LISTENERS                --->        存储已配置的 TriggerListener 的信息
            QRTZ_TRIGGERS                    --->        存储已配置的 Trigger 的信息

            注: 所有的表默认以前缀QRTZ_开始。
             可以通过在 quartz.properties 配置修改(org.quartz.jobStore.tablePrefix = QRTZ_)。

             可以对不同的Scheduler实例使用多套的表,通过改变前缀来实现。

       (5)、优化 quartz数据表结构
            a、对关键查询路径字段建立索引
           create index idx_qrtz_t_next_fire_time on QRTZ_TRIGGERS(NEXT_FIRE_TIME);
           create index idx_qrtz_t_state on QRTZ_TRIGGERS(TRIGGER_STATE);
           create index idx_qrtz_t_nf_st on QRTZ_TRIGGERS(TRIGGER_STATE,NEXT_FIRE_TIME);
           create index idx_qrtz_ft_trig_group on QRTZ_FIRED_TRIGGERS(TRIGGER_GROUP);
           create index idx_qrtz_ft_trig_name on QRTZ_FIRED_TRIGGERS(TRIGGER_NAME);
           create index idx_qrtz_ft_trig_n_g on QRTZ_FIRED_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP);
           create index idx_qrtz_ft_trig_inst_name on QRTZ_FIRED_TRIGGERS(INSTANCE_NAME);
           create index idx_qrtz_ft_job_name on QRTZ_FIRED_TRIGGERS(JOB_NAME);
           create index idx_qrtz_ft_job_group on QRTZ_FIRED_TRIGGERS(JOB_GROUP);

            b、根据Mysql innodb表结构特性,调整主键,降低二级索引的大小
           ALTER TABLE QRTZ_TRIGGERS
           ADD UNIQUE KEY IDX_NAME_GROUP(TRIGGER_NAME,TRIGGER_GROUP),
           DROP PRIMARY KEY,
           ADD ID INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
           ADD PRIMARY KEY (ID);
           ALTER TABLE QRTZ_JOB_DETAILS
           ADD UNIQUE KEY IDX_NAME_GROUP(JOB_NAME,JOB_GROUP),
           DROP PRIMARY KEY,
           ADD ID INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
           ADD PRIMARY KEY (ID);

二、Quartz集群
    1、前提
       只有使用持久的JobStore才能完成Quqrtz集群

    2、结构图
       一个 Quartz 集群中的每个节点是一个独立的 Quartz 应用,它又管理着其他的节点。
       需要分别对每个节点分别启动或停止。不像应用服务器的集群,独立的 Quartz 节点并不与另一个节点或是管理节点通信。
       Quartz 应用是通过数据库表来感知到另一应用。

    3、配置集群
       <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
       <!-- 集群配置 -->
       <prop key="org.quartz.jobStore.isClustered">true</prop>
       <prop key="org.quartz.jobStore.clusterCheckinInterval">15000</prop>
       <prop key="org.quartz.jobStore.maxMisfiresToHandleAtATime">1</prop>
       <!-- 数据源配置 使用DBCP连接池 数据源与dataSource一致 -->
       <prop key="org.quartz.jobStore.dataSource">myDS</prop>
       <prop key="org.quartz.dataSource.myDS.driver">${database.driverClassName}</prop>
       <prop key="org.quartz.dataSource.myDS.URL">${database.url}</prop>
       <prop key="org.quartz.dataSource.myDS.user">${database.username}</prop>
       <prop key="org.quartz.dataSource.myDS.password">${database.password}</prop>
       <prop key="org.quartz.dataSource.myDS.maxConnections">5</prop>

    4、配置解释
       (1)、org.quartz.jobStore.class=JobStoreTX
            将任务持久化到数据中。因为集群中节点依赖于数据库来传播Scheduler实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。

       (2)、org.quartz.jobStore.isClustered=true
            通知Scheduler实例要它参与到一个集群当中。

       (3)、org.quartz.jobStore.clusterCheckinInterval
            定义了Scheduler 实例检入到数据库中的频率(单位:毫秒)。

            Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。

            通过检入操作,Scheduler 也会更新自身的状态记录。clusterChedkinInterval 越小,Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)

二、集群实现分析
    1、Quartz源码分析:
       (1)、原理
            基于数据库表锁实现多Quartz_Node 对Job,Trigger,Calendar等同步机制

       (2)、数据库锁定表 -- QRTZ_LOCKS
            -- 数据库锁定表
            CREATE TABLE `QRTZ_LOCKS` (
              `LOCK_NAME` varchar(40) NOT NULL,
              PRIMARY KEY (`LOCK_NAME`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

            -- 记录
            +-----------------+
            | LOCK_NAME       |
            +-----------------+
            | CALENDAR_ACCESS |
            | JOB_ACCESS      |
            | MISFIRE_ACCESS  |
            | STATE_ACCESS    |
            | TRIGGER_ACCESS  |
            +-----------------+

       (3)、org.quartz.impl.jdbcjobstore.StdRowLockSemaphore   (继承 于 org.quartz.impl.jdbcjobstore.DBSemaphore)
            a、作用
           通过行级别锁实现多节点处理

            b、常量SQL -- 锁定SQL语句
           public static final String SELECT_FOR_LOCK = "SELECT * FROM " + TABLE_PREFIX_SUBST + TABLE_LOCKS + " WHERE " + COL_LOCK_NAME + " = ? FOR UPDATE";

            c、继承方法( 源至 org.quartz.impl.jdbcjobstore.DBSemaphore)
           获取QRTZ_LOCKS行级锁            ---->        public boolean obtainLock(Connection conn, String lockName)

           释放QRTZ_LOCKS行级锁            ---->        public void releaseLock(Connection conn, String lockName)

            d、核心方法
           指定锁定SQL                ---->        protected void executeSQL(Connection conn, String lockName, String expandedSQL)

       (5)、org.quartz.impl.jdbcjobstore.JobStoreTX
            a、作用
           控制并发代码,自身管理事务!

            b、继承方法( 源至 org.quartz.impl.jdbcjobstore.JobStoreSupport)
           /**
            * 执行给定的回调任选具有获得性给定的锁。
            * @param lockName
            */
            protected Object executeInNonManagedTXLock(String lockName, TransactionCallback txCallback) throws JobPersistenceException {
                boolean transOwner = false;
                Connection conn = null;
                try {
                    if (lockName != null) {
                        // If we aren't using db locks, then delay getting DB connection
                        // until after acquiring the lock since it isn't needed.
                        if (getLockHandler().requiresConnection()) {
                            conn = getNonManagedTXConnection();
                        }
                        //获取锁
                        transOwner = getLockHandler().obtainLock(conn, lockName);
                    }
                    if (conn == null) {
                        conn = getNonManagedTXConnection();
                    }
                    //回调需要执行的sql语句如:(更新Trigger为运行中(ACQUIRED),删除执行过的Trigger等)
                    Object result = txCallback.execute(conn);
                    //JobStoreTX自身维护事务
                    commitConnection(conn);
                    Long sigTime = clearAndGetSignalSchedulingChangeOnTxCompletion();
                    if(sigTime != null && sigTime >= 0) {
                        signalSchedulingChangeImmediately(sigTime);
                    }
                    return result;
                } catch (JobPersistenceException e) {
                    rollbackConnection(conn);
                    throw e;
                } catch (RuntimeException e) {
                    rollbackConnection(conn);
                    throw new JobPersistenceException("Unexpected runtime exception: " + e.getMessage(), e);
                } finally {
                    try {
                        //释放锁
                        releaseLock(conn, lockName, transOwner);
                    } finally {
                        cleanupConnection(conn);
                    }
                }
            }

            c、核心方法
           protected Object executeInLock(String lockName, TransactionCallback txCallback) throws JobPersistenceException {
               return executeInNonManagedTXLock(lockName, txCallback);
           }

       (6)、JobStoreCMT
            a、作用
           控制并发代码,事务交由容器管理!

            b、核心方法
            /**
             * 执行具有选择性的收购给锁定的回调。
             */
            protected Object executeInLock(String lockName, TransactionCallback txCallback) throws JobPersistenceException {
                boolean transOwner = false;
                Connection conn = null;
                try {
                    if (lockName != null) {
                        // If we aren't using db locks, then delay getting DB connection
                        // until after acquiring the lock since it isn't needed.
                        if (getLockHandler().requiresConnection()) {
                            conn = getConnection();
                        }
                        transOwner = getLockHandler().obtainLock(conn, lockName);
                    }

                    if (conn == null) {
                        conn = getConnection();
                    }
                    //没有事务提交操作,与任务共享一个事务
                    return txCallback.execute(conn);
                } finally {
                    try {
                        releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner);
                    } finally {
                        cleanupConnection(conn);
                    }
                }
            }


版权声明:本文为博主原创文章,未经博主允许不得转载。

Quartz集群原理及配置应用,实例使用

使用 Quartz 需要数据库内建立12张表,如下。建表顺序需要注意。因为有的表有外键, /*DDL 信息*/------------ # 建表顺序 1 CREATE TABLE `qrtz_cale...
  • zhen340417593
  • zhen340417593
  • 2016年12月09日 10:43
  • 2354

Quartz集群动态配置demo

controller+xml配置+class package com.zihai.controller.test; import static org.quartz.CronScheduleBui...
  • lylhjh
  • lylhjh
  • 2016年10月19日 14:56
  • 835

Quartz集群实战及原理解析

在调度的技术选型上, 有的人造了轮子, 有的人填了无数坑, 而我默默地选择了......
  • wenniuwuren
  • wenniuwuren
  • 2015年05月20日 09:38
  • 7566

浅析Quartz的集群配置

1、基本信息:      Quartz是一个开源的作业调度框架,它完全由java写成,并设计用于J2Se和J2EE应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复...
  • tayanxunhua
  • tayanxunhua
  • 2014年02月17日 18:19
  • 26009

Spring之——quartz集群的问题及解决方案(基于Spring4.0+quartz2.2.1的集群架构)

特别注意一点,与Spring3.1以下版本整合必须使用Quartz1,最初我拿2.2.1的,怎么搞都报错: Caused by: org.springframework.beans.factory.C...
  • l1028386804
  • l1028386804
  • 2015年10月15日 11:37
  • 9362

Springboot集成quartz之集群模式(第三期)

Springboot集成quartz之集群模式(第三期)本期将提供quartz集群能力 集群案例分析: 上一期的邮件发送功能,若在服务需要部署多节点,但定时任务不支持集群,因此,多节点定时任务势必会...
  • musuny
  • musuny
  • 2017年07月22日 22:45
  • 2950

作业调度框架 Quartz 学习笔记(一) -- HelloWorld !!!

关于定时任务的操作方法,java语言本身具有 Timer 来解决,但Timer 作用起来不是特别的舒服,由于项目的需要,使用了Quartz 这个调度框架,现把学习过程记录下来,方便以后查阅. 本教程...
  • lnara
  • lnara
  • 2013年03月04日 11:35
  • 8517

quartz详解3:quartz数据库集群-锁机制

http://blog.itpub.net/11627468/viewspace-1764753/ 一、quartz数据库锁 其中,QRTZ_LOCKS就是Quartz集群实现同步机制...
  • guolong1983811
  • guolong1983811
  • 2016年05月25日 20:17
  • 4113

项目中使用Quartz集群分享

CRM使用Quartz集群分享  一:CRM对定时任务的依赖与问题  二:什么是quartz,如何使用,集群,优化  三:CRM中quartz与Spring结合使用  1:CRM对定时任务...
  • achuo
  • achuo
  • 2015年08月10日 10:40
  • 784

SpringCloud(第 054 篇)简单 Quartz-Cluster 微服务,采用注解配置 Quartz 分布式集群

SpringCloud(第 054 篇)简单 Quartz-Cluster 微服务,采用注解配置 Quartz 分布式集群-一、大致介绍1、因网友提到有没有采用注解式配置的Quartz例子,因此本人就...
  • YLIMH_HMILY
  • YLIMH_HMILY
  • 2017年12月17日 15:17
  • 164
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:quartz学习四--存储、集群
举报原因:
原因补充:

(最多只允许输入30个字)