Quartz集群

缘何拔取Quartz:
1)资历够老,创造于1998年,比struts1还早,但是平昔在立异(27 April(April) 2012:
Quartz 2.1.5 Released),文档齐全.
2)完全由Java写成,设计用来J2SE和J2EE应用.方便集成:JVM,RMI.
3)设计清晰简单:大旨概念scheduler,trigger,job,jobDetail,listener,calendar
4)援助集群:org.quartz.jobStore.isClustered
5)扶助任务復苏:requestsRecovery
http://www.quartz-scheduler.org 获取最新Quartz 1)学习Quartz
图片 1 图1
介绍了quartz关键的组件和概括流程
(1)Quartz 的目录结构和情节
docs/api                                      Quartz 框架的JavaDoc Api
表明文档 docs/dbTables                            创造 Quartz
的数据库对象的台本 docs/wikidocs                             Quartz
的协助文件,点击 index.html 起始查阅
Examples                                    多地点采用 Quartz 的例证Lib
Quartz 使用到的第三方包 src/java/org/quartz                      使用
Quartz 的客户端程序源代码,公有 API
src/java/org/quartz/core              使用 Quartz
的劳动端程序源代码,私有 API src/java/org/quartz/simpl            Quartz
提供的不衣赖于第三方产品的简要实现 src/java/org/quartz/impl             
倚重于第三方产品的帮忙模块的实现 src/java/org/quartz/utils             
整个框架要用到的帮忙类和工具组件
src/jboss                                     提供了一定于 JBoss
特性的源代码 src/oracle                                   提供了一定于
Oracle 特性的源代码 src/weblogic                             
提供了特定于 WebLogic 特性的源代码
Quartz 框架包含众多的类和接口,它们分布在大概 11
个包中。多数所要使用到的类或接口放置在 org.quartz 包中。这一个蕴含盖了
Quartz 框架的公有 API.
(2)Quartz主旨接口 Scheduler
图片 2 图2 Scheduler 是 Quartz
的严重性 API。与Quartz大部分相互是发出于 Scheduler
之上的。客服端与Scheduler 交互是通过org.quartz.Scheduler接口。
Scheduler的实现:对艺术调用会传递到 QuartzScheduler
实例上。QuartzScheduler
对于客户端是不可见的,并且也不存在与此实例的第一手互动。
图片 3
图3
开创Scheduler Quartz 框架提供了
org.quartz.SchedulerFactory 接口。 SchedulerFactory 实例就是用来发出
Scheduler 实例的。当 Scheduler
实例被创建之后,就会存到一个仓房中(org.quartz.impl.SchedulerRepository).
Scheduler 工厂分别是 org.quartz.impl.DirectSchedulerFactory 和
org.quartz.impl.StdSchedulerFactory DirectSchedulerFactory
是为精细化控制 Scheduler
实例暴发的厂子类,一般不用,但是有利于领会quartz内部零件。

Java代码
图片 4 图片 5图片 6)

  1. — 最简单  
  2. public void createScheduler(ThreadPool threadPool, JobStore jobStore);  
  3. — 最复杂  
  4. public void createScheduler(String schedulerName, String schedulerInstanceId,ThreadPool threadPool, JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort);  

    — 最简单
    public void createScheduler(ThreadPool threadPool, JobStore jobStore);
    — 最复杂
    public void createScheduler(String schedulerName, String schedulerInstanceId,ThreadPool threadPool, JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort);

Java代码
图片 7 图片 8图片 9)

  1. public scheduler createScheduler(){  
  2.  DirectSchedulerFactory factory=DirectSchedulerFactory.getInstance();  
  3.  try {  
  4.     //创设线程池  
  5.     SimpleThreadPool threadPool = new SimpleThreadPool(10, Thread.NORM_PRIORITY);  
  6.     threadPool.initialize();  
  7.     //创建job存储类  
  8.     JobStoreTX jdbcJobStore = new JobStoreTX();  
  9.     jdbcJobStore.setDataSource(“someDatasource”);  
  10.         jdbcJobStore.setPostgresStyleBlobs(true);  
  11.         jdbcJobStore.setTablePrefix(“QRTZ_”);  
  12.         jdbcJobStore.setInstanceId(“My Instance”);  
  13.       
  14.     logger.info(“Scheduler starting up…”);  
  15.     factory.createScheduler(threadPool,jdbcJobStore);  
  16.     // Get a scheduler from the factory  
  17.         Scheduler scheduler = factory.getScheduler();  
  18.   
  19.     // 必须启动scheduler  
  20.         scheduler.start();  
  21.         return scheduler;  
  22.     }  
  23.         return null;  
  24. }  

    public scheduler createScheduler(){

     DirectSchedulerFactory factory=DirectSchedulerFactory.getInstance();
     try {
        //创建线程池
        SimpleThreadPool threadPool = new SimpleThreadPool(10, Thread.NORM_PRIORITY);
        threadPool.initialize();
        //创建job存储类
        JobStoreTX jdbcJobStore = new JobStoreTX();
        jdbcJobStore.setDataSource("someDatasource");
        jdbcJobStore.setPostgresStyleBlobs(true);
        jdbcJobStore.setTablePrefix("QRTZ_");
        jdbcJobStore.setInstanceId("My Instance");
    
        logger.info("Scheduler starting up...");
        factory.createScheduler(threadPool,jdbcJobStore);
        // Get a scheduler from the factory
        Scheduler scheduler = factory.getScheduler();
    
        // 必须启动scheduler
        scheduler.start();
        return scheduler;
    }
        return null;
    

    }

org.quartz.impl.StdSchedulerFactory
倚重于属性类(Properties)决定哪些生产 Scheduler 实例
因而加载属性文件,Properties 提供启动参数:

Java代码
图片 10 图片 11图片 12)

  1. public scheduler createScheduler(){  
  2.     // Create an instance of the factory  
  3.     StdSchedulerFactory factory = new StdSchedulerFactory();  
  4.       
  5.     // Create the properties to configure the factory  
  6.     Properties props = new Properties();  
  7.     // required to supply threadpool class and num of threads  
  8.     props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,”org.quartz.simpl.SimpleThreadPool”);  
  9.     props.put(“org.quartz.threadPool.threadCount”, “10”);  
  10.       
  11.     try {  
  12.         // Initialize the factory with properties  
  13.         factory.initialize(props);  
  14.         Scheduler scheduler = factory.getScheduler();  
  15.         logger.info(“Scheduler starting up…”);  
  16.         scheduler.start();  
  17.     } catch (SchedulerException ex) {  
  18.         logger.error(ex);  
  19.     }  
  20. }  

    public scheduler createScheduler(){

    // Create an instance of the factory
    StdSchedulerFactory factory = new StdSchedulerFactory();
    
    // Create the properties to configure the factory
    Properties props = new Properties();
    // required to supply threadpool class and num of threads
    props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,"org.quartz.simpl.SimpleThreadPool");
    props.put("org.quartz.threadPool.threadCount", "10");
    
    try {
        // Initialize the factory with properties
        factory.initialize(props);
        Scheduler scheduler = factory.getScheduler();
        logger.info("Scheduler starting up...");
        scheduler.start();
    } catch (SchedulerException ex) {
        logger.error(ex);
    }
    

    }

调用静态方法
getDefaultScheduler() 方法中调用了空的构造方法。假若在此之前未调用过另外一个
initialize() 方法,那么无参的initialize()方法会被调用。这会初阶去依据上面说的逐条加载文件。
默认情形下,quartz.properties 会被固定到,并从中加载属性。
properties加载顺序: 1. 检查 System.getProperty(“org.quartz.properties”)
中是否设置了此外文件名 2. 否则,使用 quartz.properties
作为要加载的文本名 3. 总计从当前工作目录中加载这个文件 4. 盘算从系统
classpath 下加载这多少个文件 在 Quartz Jar 包中有一个默认的
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
= true org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
到此成立Scheduler完成
通过Scheduler领悟Quartz Scheduler 的 API 可以分结合以下五个体系: ·管理
Scheduler
(1)启动 Scheduler

Java代码
图片 13 图片 14图片 15)

  1. //Start the scheduler  
  2.  scheduler.start();  

    //Start the scheduler
    scheduler.start();

start()方法被调用,Scheduler 就起来物色需要履行的 Job。在你刚到手一个 Scheduler
新的实例时,或者 Scheduler 被安装为 standby 情势后,你才可以调用 start()方法。

Java代码
图片 16 图片 17图片 18)

  1. public void standby() throws SchedulerException;  

    public void standby() throws SchedulerException;

若是调用了
shutdown() 方法之后,你就无法再调用 Scheduler 实例的 start() 方法了。
这是因为 shutdown() 方法销毁了为 Scheduler
成立的兼具的资源(线程,数据库连接等)。 你也许需要Standby 形式:设置
Scheduler 为 standby 情势会招致 Scheduler搜寻要履行的 Job
的线程被搁浅下来
停止 Scheduler

Java代码
图片 19 图片 20图片 21)

  1. //waitFor乔布斯ToComplete 是否让眼前正值开展的Job正常实施到位才停止Scheduler  
  2. public void shutdown(boolean waitForJobsToComplete) throws SchedulerException;  
  3. public void shutdown() throws SchedulerException;  

    //waitFor乔布斯ToComplete 是否让眼前正在举办的Job正常履行到位才平息Scheduler
    public void shutdown(boolean waitFor乔布斯ToComplete) throws SchedulerException;
    public void shutdown() throws SchedulerException;

其他管理Scheduler 方法见API… 管理 Job 什么是
Quartz Job? 一个Quart
Job就是一个别样一个后续job或job子接口的Java类,你能够用这个类做此外业务!
org.quartz.Job 接口

Java代码
图片 22 图片 23图片 24)

  1. public void execute(JobExecutionContext context)throws JobExecutionException;  
  2. JobExecutionContext  

    public void execute(JobExecutionContext context)throws JobExecutionException;
    JobExecutionContext

当 Scheduler
调用一个 Job,一个 JobexecutionContext 传递给 execute()方法。JobExecutionContext 对象让 Job 能 访问 Quartz 运行时候条件和 Job
本身的数目。类似于在 Java Web 应用中的 servlet 访问 ServletContext 。
通过 JobExecutionContext,Job 可访问到所处环境的所有音信,包括注册到
Scheduler 上与该 Job 相关联的 JobDetail 和 Trigger。 JobDetail 部署在
Scheduler 上的每一个 Job 只开创了一个 JobDetail实例。JobDetail 是用作
Job 实例举行定义的 // Create the JobDetail JobDetail jobDetail = new
JobDetail(“PrintInfoJob”,Scheduler.DEFAULT_GROUP, PrintInfoJob.class);
// Create a trigger that fires now and repeats forever Trigger trigger =
TriggerUtils.makeImmediateTrigger( SimpleTrigger.REPEAT_INDEFINITELY,
10000); trigger.setName(“PrintInfoJobTrigger”);// register with the
Scheduler scheduler.scheduleJob(jobDetail, trigger); JobDetail 被加到
Scheduler 中了,而不是 job。Job 类是当做 JobDetail
的一部份,job直到Scheduler准备要推行它的时候才会被实例化的,因而job不存在线成安全性问题.
使用 JobDataMap 对象设定 Job 状态

Java代码
图片 25 图片 26图片 27)

  1. public void executeScheduler() throws SchedulerException{  
  2.     scheduler = StdSchedulerFactory.getDefaultScheduler();  
  3.     scheduler.start();  
  4.     logger.info(“Scheduler was started at ” + new Date());  
  5.     // Create the JobDetail  
  6.     JobDetail jobDetail = new JobDetail(“PrintJobDataMapJob”,Scheduler.DEFAULT_GROUP,PrintJobDataMapJob.class);  
  7.     // Store some state for the Job  
  8.     jobDetail.getJobDataMap().put(“name”, “John Doe”);  
  9.     jobDetail.getJobDataMap().put(“age”, 23);  
  10.     jobDetail.getJobDataMap().put(“balance”,new BigDecimal(1200.37));  
  11.     // Create a trigger that fires once  
  12.     Trigger trigger = TriggerUtils.makeImmediateTrigger(0, 10000);  
  13.     trigger.setName(“PrintJobDataMapJobTrigger”);  
  14.     scheduler.scheduleJob(jobDetail, trigger);  
  15. }  
  16. //Job 能透过 JobExecutionContext 对象访问 JobDataMap  
  17. public class PrintJobDataMapJob implements Job {  
  18.     public void execute(JobExecutionContext context)throws JobExecutionException {  
  19.         logger.info(“in PrintJobDataMapJob”);  
  20.         // Every job has its own job detail  
  21.         JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();  
  22.         // Iterate through the key/value pairs  
  23.         Iterator iter = jobDataMap.keySet().iterator();  
  24.         while (iter.hasNext()) {  
  25.             Object key = iter.next();  
  26.             Object value = jobDataMap.get(key);  
  27.             logger.info(“Key: ” + key + ” – Value: ” + value);  
  28.         }  
  29.     }  
  30.  }  

    public void executeScheduler() throws SchedulerException{

    scheduler = StdSchedulerFactory.getDefaultScheduler();
    scheduler.start();
    logger.info("Scheduler was started at " + new Date());
    // Create the JobDetail
    JobDetail jobDetail = new JobDetail("PrintJobDataMapJob",Scheduler.DEFAULT_GROUP,PrintJobDataMapJob.class);
    // Store some state for the Job
    jobDetail.getJobDataMap().put("name", "John Doe");
    jobDetail.getJobDataMap().put("age", 23);
    jobDetail.getJobDataMap().put("balance",new BigDecimal(1200.37));
    // Create a trigger that fires once
    Trigger trigger = TriggerUtils.makeImmediateTrigger(0, 10000);
    trigger.setName("PrintJobDataMapJobTrigger");
    scheduler.scheduleJob(jobDetail, trigger);
    

    }
    //Job 能通过 JobExecutionContext 对象访问 JobDataMap
    public class PrintJobDataMapJob implements Job {

    public void execute(JobExecutionContext context)throws JobExecutionException {
        logger.info("in PrintJobDataMapJob");
        // Every job has its own job detail
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        // Iterate through the key/value pairs
        Iterator iter = jobDataMap.keySet().iterator();
        while (iter.hasNext()) {
            Object key = iter.next();
            Object value = jobDataMap.get(key);
            logger.info("Key: " + key + " - Value: " + value);
        }
    }
    

    }

在Quartz
1.5以后,JobDataMap在 Trigger
级也是可用的。它的用途类似于Job级的JobDataMap,补助在同一个JobDetail上的四个Trigger。
伴随着进入到 Quartz 1.5 中的这一增高特性,可以运用 JobExecutionContext
的一个新的更便宜的点子得到到 Job 和 Trigger 级的并集的 map 中的值。
这么些方法就是getMergedJobDataMap() 取job 和 Trigger级的并集map,它亦可在
Job 中利用。管法推荐使用这些方法.
* 实际应用中trigger级别有时取不到map中的值, 使用getMergedJobDataMap
可以收获到(官方推荐此情势).
有气象的Job: org.quartz.StatefulJob 接口 当需要在五次 Job
执行间保障状态,使用StatefulJob 接口.
Job 和 StatefulJob 在框架中采纳中设有多少个重大区别。 (一) JobDataMap
在每一次执行之后再也持久化到 JobStore 中。那样就保证您对 Job
数据的更动直到下次履行依旧保持着。 (二) 五个或五个有状态的 JobDetail
实例不可能并发执行。保证JobDataMap线程安全
只顾:实际应用时采纳jobStoreTX/jobStoreCMT
,StatefulJob,大量的trigger对应一个JobDetail的场馆下Mysql会生出锁超时问题.
停顿 Job Quartz 包括一个接口叫做
org.quartz.InterruptableJob,它增添了平凡的 Job 接口并提供了一个
interrupt() 方法: 没有尖锐钻探,只略知一二 Scheduler会调用自定义的Job的
interrupt()方法。由用户决定 Job 决定哪些中断.没有测试!!!
job的性状 易失性 volatility 一个易失性的 Job
是在先后关闭之后不会被持久化。一个 Job 是透过调用 JobDetail 的
setVolatility(true)被设置为易失. Job易失性的默认值是 false.
注意:只有选择持久性JobStore时才使得
Job 持久性 durability 设置JobDetail 的
setDurability(false),在有着的触发器触发之后JobDetail将从 JobStore
中移出。 Job持久性默认值是false. 
Scheduler将移除没有trigger关联的jobDetail
Job 可復苏性 shuldRecover
当一个Job在执行中,Scheduler非正常的关门,设置JobDetail
的setRequestsRecovery(true) 在 Scheduler
重启之后可过来的Job还会另行被实践。这多少个 Job
会重新先河推行。注意job代码事务特性.
Job可复原性默认为false,Scheduler不会试着去复苏job操作。
图片 28
图为表明没有举行到位的job数据库记录
Scheduler 中移除 Job 移除所有与这多少个 Job 相关联的 Trigger;假若这么些 Job
是非持久性的,它将会从 Scheduler 中移出。 更直接的办法是运用 deleteJob()方法,它还会去除所有与当下job关联的trigger
public boolean deleteJob(String jobName, String groupName) throws
SchedulerException; quartz 本身提供的 Job org.quartz.jobs.FileScanJob
检查某个指定文件是否变动,并在文书被更改时通报到相应监听器的 Job
org.quartz.jobs.FileScanListener 在文件被涂改后通报 FileScanJob 的监听器
org.quartz.jobs.NativeJob 用来推行本地程序(如 windows 下 .exe 文件) 的
Job org.quartz.jobs.NoOpJob
什么也不做,但用来测试监听器不是很有用的。一些用户甚至仅仅用它来促成一个监听器的运行
org.quartz.jobs.ee.mail.SendMailJob 使用 JavaMail API 发送 e-mail 的 Job
org.quartz.jobs.ee.jmx.JMXInvokerJob 调用 JMX bean 上的措施的 Job
org.quartz.jobs.ee.ejb.EJBInvokerJob 用来调用 EJB 上艺术的 Job
job的通晓到此停止
知情quartz Trigger Job
包含了要履行任务的逻辑,但是Job不负担何时实施。这么些工作由触发器(Trigger)负责。
Quartz Trigger继承了抽象的org.quartz.Trigger 类。 目前,Quartz
有两个可用的落实
org.quartz.SimpleTrigger org.quartz.CronTrigger
org.quartz.NthIncludeDayTrigger 使用org.quartz.SimpleTrigger
SimpleTrigger 是设置和采取是最最简练的一种 Quartz
Trigger。它是为这种需要在特定的日期/时间启动,且以一个或许的间隔时间重复执行
n 次的 Job 所计划的。
SimpleTrigger
存在多少个变种的构造方法。他们是从无参的本子平昔到带任何参数的版本。
下面代码版断凸显了一个仅包含trigger 的名字和组的简练构造方法
SimpleTrigger sTrigger = new SimpleTrigger(“myTrigger”,
Scheduler.DEFAULT_GROUP); 那一个 Trigger
会顿时施行,而不重复。还有一个构造方法带有六个参数,配置 Triiger
在某一特定时刻触发,重复执行多次,和五遍触发间的延迟时间。

Java代码
图片 29 图片 30图片 31)

  1. public SimpleTrigger(String name, String group,String jobName, String jobGroup,  
  2.  Date startTime,Date endTime, int repeatCount, long repeatInterval);  

    public SimpleTrigger(String name, String group,String jobName, String jobGroup,
    Date startTime,Date endTime, int repeatCount, long repeatInterval);

行使org.quartz.CronTrigger CronTrigger 是遵照Unix 类似于 cron 的表明式触发,也是效率最精锐和最常用的Trigger 
Cron表明式:

Java代码
图片 32 图片 33图片 34)

  1. “0 0 12 * * ?”                     Fire at 12pm (noon) every day  
  2. “0 15 10 ? * *”                   Fire at 10:15am every day  
  3. “0 15 10 * * ?”                   Fire at 10:15am every day  
  4. “0 15 10 * * ? *”                 Fire at 10:15am every day  
  5. “0 15 10 * * ? 2005”           Fire at 10:15am every day during the year 2005  
  6. “0 * 14 * * ?”                     Fire every minute starting at 2pm and ending at 2:59pm, every day  
  7. “0 0/5 14 * * ?”                  Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day  
  8. “0 0/5 14,18 * * ?”              Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day  
  9. “0 0-5 14 * * ?”                   Fire every minute starting at 2pm and ending at 2:05pm, every day  
  10. “0 10,44 14 ? 3 WED”         Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.  
  11. “0 15 10 ? * MON-FRI”        Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday  
  12. “0 15 10 15 * ?”                  Fire at 10:15am on the 15th day of every month  
  13. “0 15 10 L * ?”                    Fire at 10:15am on the last day of every month  
  14. “0 15 10 ? * 6L”                   Fire at 10:15am on the last Friday of every month  
  15. “0 15 10 ? * 6L”                   Fire at 10:15am on the last Friday of every month  
  16. “0 15 10 ? * 6L 2002-2005”   Fire at 10:15am on every last Friday of every month during the years 2002, 2003, 2004 and 2005  
  17. “0 15 10 ? * 6#3”                 Fire at 10:15am on the third Friday of every month  

    “0 0 12 ?” Fire at 12pm (noon) every day
    “0 15 10 ? ” Fire at 10:15am every day
    “0 15 10 ?” Fire at 10:15am every day
    “0 15 10 ? ” Fire at 10:15am every day
    “0 15 10
    ? 2005″ Fire at 10:15am every day during the year 2005
    “0
    14 ?” Fire every minute starting at 2pm and ending at 2:59pm, every day
    “0 0/5 14 ?” Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day
    “0 0/5 14,18 ?” Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day
    “0 0-5 14 ?” Fire every minute starting at 2pm and ending at 2:05pm, every day
    “0 10,44 14 ? 3 WED” Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.
    “0 15 10 ? MON-FRI” Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday
    “0 15 10 15
    ?” Fire at 10:15am on the 15th day of every month
    “0 15 10 L ?” Fire at 10:15am on the last day of every month
    “0 15 10 ?
    6L” Fire at 10:15am on the last Friday of every month
    “0 15 10 ? 6L” Fire at 10:15am on the last Friday of every month
    “0 15 10 ?
    6L 2002-2005″ Fire at 10:15am on every last Friday of every month during the years 2002, 2003, 2004 and 2005
    “0 15 10 ? * 6#3” Fire at 10:15am on the third Friday of every month

利用
org.quartz.NthIncludedDayTrigger
org.quartz.NthIncludedDayTrigger是统筹用来在每一间隔类型的第几天执行
Job。 例如,你要在每个月的 12
号执行发工钱指示的Job。接下来的代码片断描绘了什么创设一个
NthIncludedDayTrigger.

Java代码
图片 35 图片 36图片 37)

  1. //创造每个月的12号的NthIncludedDayTrigger  
  2. NthIncludedDayTrigger trigger = new NthIncludedDayTrigger(“MyTrigger”, Scheduler.DEFAULT_GROUP);  
  3. trigger.setN(12);  
  4. trigger.setIntervalType(NthIncludedDayTrigger.INTERVAL_TYPE_MONTHLY);  

    //成立每个月的12号的NthIncludedDayTrigger

    NthIncludedDayTrigger trigger = new NthIncludedDayTrigger("MyTrigger", Scheduler.DEFAULT_GROUP);
    trigger.setN(12);
    trigger.setIntervalType(NthIncludedDayTrigger.INTERVAL_TYPE_MONTHLY);
    

jobDetail +
trigger组成最基本的定时任务: 特别注意:一个job可以对应几个Trgger ,
一个Trigger只可以对应一个job .
如:CRM中N天未拜访的job对应拥有的N天未拜访商家(一个供销社一个trigger)
大约1:1000的百分比      job和trigger都是透过name 和 group
属性确定唯一性的.
Quartz Calendar Quartz 的 Calendar
对象与 Java API 的 java.util.Calendar不同。 Java 的 Calender
对象是通用的日子和岁月工具; Quartz 的 Calender
专门用来屏闭一个时间距离,使 Trigger 在这几个距离中不被触发。
例如,让我们只要裁撤节假期执行job。
Quartz包括过多的 Calender 实现足以满意大部分的需求.
org.quartz.impl.calendar.BaseCalender 为高级的 Calender
实现了骨干的功效,实现了 org.quartz.Calender 接口
org.quartz.impl.calendar.WeeklyCalendar
排除星期中的一天或多天,例如,可用来破除周末
org.quartz.impl.calendar.MonthlyCalendar
排除月份中的数天,例如,可用于破除每月的最后一天
org.quartz.impl.calendar.AnnualCalendar 排除年中一天或多天
org.quartz.impl.calendar.HolidayCalendar 特其它用来从 Trigger
中革除节假期
应用Calendar,只需实例化后并插手你要破除的日期,然后用 Scheduler
注册,最终必须让Calender依附于Trigger实例。
免除国庆节实例

Java代码
图片 38 图片 39图片 40)

  1. private void scheduleJob(Scheduler scheduler, Class jobClass) {  
  2.     try {  
  3.         // Create an instance of the Quartz AnnualCalendar  
  4.         AnnualCalendar cal = new AnnualCalendar();  
  5.         // exclude 国庆节  
  6.         Calendar gCal = GregorianCalendar.getInstance();  
  7.         gCal.set(Calendar.MONTH, Calendar.OCTOBER);  
  8.         List<Calendar> mayHolidays = new ArraysList<Calendar>();  
  9.         for(int i=1; i<=7; i++){  
  10.             gCal.set(Calendar.DATE, i);  
  11.             mayHolidays.add(gCal);  
  12.         }  
  13.         cal.setDaysExcluded(mayHolidays);  
  14.         // Add to scheduler, replace existing, update triggers  
  15.         scheduler.addCalendar(“crmHolidays”, cal, true, true);  
  16.         /* 
  17.         * Set up a trigger to start firing now, repeat forever 
  18.         * and have (60000 ms) between each firing. 
  19.         */  
  20.         Trigger trigger = TriggerUtils.makeImmediateTrigger(“myTrigger”,-1,60000);  
  21.         // Trigger will use Calendar to exclude firing times  
  22.         trigger.setCalendarName(“crmHolidays”);  
  23.         JobDetail jobDetail = new JobDetail(jobClass.getName(), Scheduler.DEFAULT_GROUP, jobClass);  
  24.         // Associate the trigger with the job in the scheduler  
  25.         scheduler.scheduleJob(jobDetail, trigger);  
  26.     } catch (SchedulerException ex) {  
  27.         logger.error(ex);  
  28.     }  
  29. }  

    private void scheduleJob(Scheduler scheduler, Class jobClass) {

    try {
        // Create an instance of the Quartz AnnualCalendar
        AnnualCalendar cal = new AnnualCalendar();
        // exclude 国庆节
        Calendar gCal = GregorianCalendar.getInstance();
        gCal.set(Calendar.MONTH, Calendar.OCTOBER);
        List<Calendar> mayHolidays = new ArraysList<Calendar>();
        for(int i=1; i<=7; i++){
            gCal.set(Calendar.DATE, i);
            mayHolidays.add(gCal);
        }
        cal.setDaysExcluded(mayHolidays);
        // Add to scheduler, replace existing, update triggers
        scheduler.addCalendar("crmHolidays", cal, true, true);
        /*
        * Set up a trigger to start firing now, repeat forever
        * and have (60000 ms) between each firing.
        */
        Trigger trigger = TriggerUtils.makeImmediateTrigger("myTrigger",-1,60000);
        // Trigger will use Calendar to exclude firing times
        trigger.setCalendarName("crmHolidays");
        JobDetail jobDetail = new JobDetail(jobClass.getName(), Scheduler.DEFAULT_GROUP, jobClass);
        // Associate the trigger with the job in the scheduler
        scheduler.scheduleJob(jobDetail, trigger);
    } catch (SchedulerException ex) {
        logger.error(ex);
    }
    

    }

Quartz 监听器
Quartz 提供了三序列型的监听器:监听Job,监听Trigger,和监听Scheduler.
监听器是用作扩充点存在的. Quartz
监听器是扩充点,可以扩充框架并定制来做一定的作业。跟Spring,Hibernate,Servlet监听器类似.
实现监听 1. 创制一个 Java 类,实现监听器接口 2.
用你的行使中一定的逻辑实现监听器接口的持有办法 3. 注册监听器
大局和非全局监听器 JobListener 和
TriggerListener
可被登记为全局或非全局监听器。一个大局监听器能吸纳到所有的 Job/Trigger
的风波通报。 而一个非全局监听器只好接受到这一个在其上已注册了监听器的 Job
或 Triiger 的事件。
作者:詹姆士(James) 豪斯(House)描述全局和非全局监听器
全局监听器是知难而进意识的,它们为了实施它们的职责而实心的去寻找每一个或者的风波。平日,全局监听器要做的做事决不指定到特定的
Job 或 Trigger。 非全局监听器一般是消极发现的,它们在所关心的 Trigger
激发从前可能 Job
执行往日什么事也不做。因而,非全局的监听器比起全局监听器而言更契合于修改或扩张Job 执行的做事。 类似装饰设计格局 监听
Job 事件 org.quartz.JobListener 接口包含一密密麻麻的方法,它们会由 Job
在其生命周期中暴发的一点重要事件时被调用

Java代码
图片 41 图片 42图片 43)

  1. public interface JobListener {  
  2.     //命名jobListener 只对非全局监听器有效  
  3.     public String getName();  
  4.   
  5.     //Scheduler 在 JobDetail 将要被实践时调用这些艺术。  
  6.     public void jobToBeExecuted(JobExecutionContext context);  
  7.   
  8. //Scheduler 在 JobDetail 即将被实施,但又被推翻时调用这么些法子。  
  9.     public void jobExecutionVetoed(JobExecutionContext context);  
  10.   
  11. //Scheduler 在 JobDetail 被实施之后调用这一个点子。  
  12.     public void jobWasExecuted(JobExecutionContext context,JobExecutionException jobException);  

    public interface JobListener {

    //命名jobListener 只对非全局监听器有效
    public String getName();
    
    //Scheduler 在 JobDetail 将要被执行时调用这个方法。
    public void jobToBeExecuted(JobExecutionContext context);
    
    //Scheduler 在 JobDetail 即将被执行,但又被否决时调用这个方法。
    public void jobExecutionVetoed(JobExecutionContext context);
    
    //Scheduler 在 JobDetail 被执行之后调用这个方法。
    public void jobWasExecuted(JobExecutionContext context,JobExecutionException jobException);
    

    }

图片 44
图7 job listener插手job的进行生命周期
注册全局监听器

Java代码
图片 45 图片 46图片 47)

  1. scheduler.addGlobalJobListener(jobListener);  

    scheduler.addGlobalJobListener(jobListener);

登记非全局监听器(依次完成,顺序不可以颠倒)

Java代码
图片 48 图片 49图片 50)

  1. scheduler.addJobListener(jobListener);  
  2. jobDetail.addJobListener(jobListener.getName());  
  3. //要是已经存在jobDetail则覆盖.  
  4. scheduler.addjob(jobDetail,true);  

    scheduler.addJobListener(jobListener);
    jobDetail.addJobListener(jobListener.getName());
    //假设已经存在jobDetail则覆盖.
    scheduler.addjob(jobDetail,true);

监听 Trigger
事件 org.quartz.TriggerListener
接口定义Trigger监听器

Java代码
图片 51 图片 52图片 53)

  1. public interface TriggerListener {  
  2.     //命名triggerListener 只对非全局监听器有效  
  3.     public String getName();  
  4.   
  5.     //当与监听器相关联的 Trigger 被触发,Job 上的 execute() 方法就要被执行时,调用这么些主意。  
  6.     //在大局TriggerListener 情状下,这多少个模式为有着 Trigger 被调用。(不要做耗时操作)  
  7.     public void triggerFired(Trigger trigger, JobExecutionContext context);  
  8.   
  9.     //在 Trigger 触发后,Job 将要被实施时由调用这一个点子。  
  10.     //TriggerListener给了一个增选去否定 Job 的施行。假使这么些艺术再次回到 true,这一个 Job 将不会为此次 Trigger 触发而收获推行。  
  11.     public boolean vetoJobExecution(Trigger trigger, JobExecutidonContext context);  
  12.   
  13.     // Scheduler 调用那个办法是在 Trigger 错过触发时。  
  14.     // JavaDoc 提议:你应有关爱此办法中持续时间长的逻辑:在出现许多失去触发的 Trigger 时,长逻辑会导致骨牌效应。你应当保持这上艺术尽量的小  
  15.     public void triggerMisfired(Trigger trigger);  
  16.   
  17.     //Trigger 被触发并且形成了Job的执行时调用这个点子。  
  18.     public void triggerComplete(Trigger trigger, JobExecutionContext context, int triggerInstructionCode);  
  19. }  

    public interface TriggerListener {

    //命名triggerListener 只对非全局监听器有效
    public String getName();
    
    //当与监听器相关联的 Trigger 被触发,Job 上的 execute() 方法将要被执行时,调用这个方法。
    //在全局TriggerListener 情况下,这个方法为所有 Trigger 被调用。(不要做耗时操作)
    public void triggerFired(Trigger trigger, JobExecutionContext context);
    
    //在 Trigger 触发后,Job 将要被执行时由调用这个方法。
    //TriggerListener给了一个选择去否决 Job 的执行。假如这个方法返回 true,这个 Job 将不会为此次 Trigger 触发而得到执行。
    public boolean vetoJobExecution(Trigger trigger, JobExecutidonContext context);
    
    // Scheduler 调用这个方法是在 Trigger 错过触发时。
    // JavaDoc 指出:你应该关注此方法中持续时间长的逻辑:在出现许多错过触发的 Trigger 时,长逻辑会导致骨牌效应。你应当保持这上方法尽量的小
    public void triggerMisfired(Trigger trigger);
    
    //Trigger 被触发并且完成了Job的执行时调用这个方法。
    public void triggerComplete(Trigger trigger, JobExecutionContext context, int triggerInstructionCode);
    

    }

triggerListener的注册与jobListener相同
监听 Scheduler 事件
org.quartz.SchedulerListener
接口定义Trigger监听器

Java代码
图片 54 图片 55图片 56)

  1. public interface SchedulerListener {  
  2.     //有新的JobDetail部署调用那多少个方法。  
  3.     public void jobScheduled(Trigger trigger);  
  4.   
  5.     //卸载时调用这么些办法。  
  6.     public void jobUnscheduled(String triggerName, String triggerGroup);  
  7.   
  8.     //当一个Trigger到达再也不会触发时调用这多少个主意。  
  9.     public void triggerFinalized(Trigger trigger);  
  10.   
  11.     //Scheduler 调用这多少个艺术是爆发在一个Trigger或五个Trigger被暂停时。假要是多少个Trigger的话,triggerName 参数将为null。  
  12.     public void triggersPaused(String triggerName, String triggerGroup);  
  13.   
  14.     //Scheduler 调用这多少个点子是爆发成一个 Trigger 或 Trigger 组从中断中復苏时。假设是六个Trigger的话,triggerName 参数将为 null。  
  15.     public void triggersResumed(String triggerName,String triggerGroup);  
  16.   
  17.     //当一个或一组 JobDetail 暂停时调用这多少个情势。  
  18.     public void jobsPaused(String jobName, String jobGroup);  
  19.   
  20.     //当一个或一组 Job 从中断上回复时调用那个办法。淌假诺四个Job,jobName参数将为 null。  
  21.     public void jobsResumed(String jobName, String jobGroup);  
  22.   
  23.     // 在Scheduler 的正规运作期间发生一个严重错误时调用这一个情势。错误的类型会各式的,不过下面列举了部分荒谬例子:  
  24.     // 可以拔取 SchedulerException 的 getErrorCode() 或者 getUnderlyingException() 方法或获得到特定错误的更详实的音讯  
  25.     public void schedulerError(String msg, SchedulerException cause);  
  26.   
  27.     //Scheduler 调用这些艺术用来打招呼 SchedulerListener Scheduler 将要被关门。  
  28.     public void schedulerShutdown();  
  29. }  

    public interface SchedulerListener {

    //有新的JobDetail部署调用这个方法。
    public void jobScheduled(Trigger trigger);
    
    //卸载时调用这个方法。
    public void jobUnscheduled(String triggerName, String triggerGroup);
    
    //当一个Trigger到达再也不会触发时调用这个方法。
    public void triggerFinalized(Trigger trigger);
    
    //Scheduler 调用这个方法是发生在一个Trigger或多个Trigger被暂停时。假如是多个Trigger的话,triggerName 参数将为null。
    public void triggersPaused(String triggerName, String triggerGroup);
    
    //Scheduler 调用这个方法是发生成一个 Trigger 或 Trigger 组从暂停中恢复时。假如是多个Trigger的话,triggerName 参数将为 null。
    public void triggersResumed(String triggerName,String triggerGroup);
    
    //当一个或一组 JobDetail 暂停时调用这个方法。
    public void jobsPaused(String jobName, String jobGroup);
    
    //当一个或一组 Job 从暂停上恢复时调用这个方法。假如是多个Job,jobName参数将为 null。
    public void jobsResumed(String jobName, String jobGroup);
    
    // 在Scheduler 的正常运行期间产生一个严重错误时调用这个方法。错误的类型会各式的,但是下面列举了一些错误例子:
    // 可以使用 SchedulerException 的 getErrorCode() 或者 getUnderlyingException() 方法或获取到特定错误的更详尽的信息
    public void schedulerError(String msg, SchedulerException cause);
    
    //Scheduler 调用这个方法用来通知 SchedulerListener Scheduler 将要被关闭。
    public void schedulerShutdown();
    

    }

注册SchedulerListener(SchedulerListener不存在大局非全局性)
scheduler.addSchedulerListener(schedulerListener);
由于scheduler非凡存在不打印问题,CRM使用监听器代码打印.

Java代码
图片 57 图片 58图片 59)

  1. public class QuartzExceptionSchedulerListener extends SchedulerListenerSupport{  
  2.     private Logger logger = LoggerFactory.getLogger(QuartzExceptionSchedulerListener.class);  
  3.     @Override  
  4.     public void schedulerError(String message, SchedulerException e) {  
  5.         super.schedulerError(message, e);  
  6.         logger.error(message, e.getUnderlyingException());  
  7.     }  
  8. }  

    public class QuartzExceptionSchedulerListener extends SchedulerListenerSupport{

    private Logger logger = LoggerFactory.getLogger(QuartzExceptionSchedulerListener.class);
    @Override
    public void schedulerError(String message, SchedulerException e) {
        super.schedulerError(message, e);
        logger.error(message, e.getUnderlyingException());
    }
    

    }

Java代码
图片 60 图片 61图片 62)

  1. <bean  id=”quartzExceptionSchedulerListener”  class=”com.***.crm.quartz.listener.QuartzExceptionSchedulerListener”></bean>  
  2. <!– 配置监听器 –>  
  3. <property name=”schedulerListeners”>  
  4.     <list>  
  5.         <ref bean=”quartzExceptionSchedulerListener”/>  
  6.     </list>  
  7. </property>  


quartz与线程
主处理线程:QuartzSchedulerThread
启动Scheduler时。QuartzScheduler被创立并成立一个org.quartz.core.QuartzSchedulerThread
类的实例。 QuartzSchedulerThread
包含有决定何时下一个Job将被触发的拍卖循环。QuartzSchedulerThread 是一个
Java 线程。它看做一个非守护线程运行在例行优先级下。
QuartzSchedulerThread 的主处理轮循步骤: 1. 当 Scheduler 正在运作时: A.
检查是不是有转移为 standby 格局的央浼。 1. 假若 standby
方法被调用,等待继续的信号 B. 询问 JobStore 下次要被触发的 Trigger. 1.
万一没有 Trigger 待触发,等候一小段日子后再次检查 2. 一旦有一个可用的
Trigger,等待触发它的贴切时间的来到 D. 时间到了,为 Trigger 获取到
triggerFiredBundle. E. 使用Scheduler和triggerFiredBundle 为 Job
创制一个JobRunShell实例 F. 在ThreadPool 申请一个线程运行 JobRunShell
实例.
代码逻辑在QuartzSchedulerThread 的 run()中,如下:

Java代码
图片 63 图片 64图片 65)

  1. /** 
  2.  * QuartzSchedulerThread.run 
  3.     * <p> 
  4.     * The main processing loop of the <code>QuartzSchedulerThread</code>. 
  5.     * </p> 
  6.     */  
  7.    public void run() {  
  8.        boolean lastAcquireFailed = false;  
  9.        while (!halted.get()) {  
  10.            try {  
  11.                // check if we’re supposed to pause…  
  12.                synchronized (sigLock) {  
  13.                    while (paused && !halted.get()) {  
  14.                        try {  
  15.                            // wait until togglePause(false) is called…  
  16.                            sigLock.wait(1000L);  
  17.                        } catch (InterruptedException ignore) {  
  18.                        }  
  19.                    }  
  20.      
  21.                    if (halted.get()) {  
  22.                        break;  
  23.                    }  
  24.                }  
  25.   
  26.                int availTreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();  
  27.                if(availTreadCount > 0) { // will always be true, due to semantics of blockForAvailableThreads…  
  28.                    Trigger trigger = null;  
  29.   
  30.                    long now = System.currentTimeMillis();  
  31.                    clearSignaledSchedulingChange();  
  32.                    try {  
  33.                        trigger = qsRsrcs.getJobStore().acquireNextTrigger(  
  34.                                ctxt, now + idleWaitTime);  
  35.                        lastAcquireFailed = false;  
  36.                    } catch (JobPersistenceException jpe) {  
  37.                        if(!lastAcquireFailed) {  
  38.                            qs.notifySchedulerListenersError(  
  39.                                “An error occured while scanning for the next trigger to fire.”,  
  40.                                jpe);  
  41.                        }  
  42.                        lastAcquireFailed = true;  
  43.                    } catch (RuntimeException e) {  
  44.                        if(!lastAcquireFailed) {  
  45.                            getLog().error(“quartzSchedulerThreadLoop: RuntimeException ”  
  46.                                    +e.getMessage(), e);  
  47.                        }  
  48.                        lastAcquireFailed = true;  
  49.                    }  
  50.   
  51.                    if (trigger != null) {  
  52.                        now = System.currentTimeMillis();  
  53.                        long triggerTime = trigger.getNextFireTime().getTime();  
  54.                        long timeUntilTrigger = triggerTime – now;  
  55.                        while(timeUntilTrigger > 2) {  
  56.                         synchronized(sigLock) {  
  57.                             if(!isCandidateNewTimeEarlierWithinReason(triggerTime, false)) {  
  58.                                 try {  
  59.                                     // we could have blocked a long while  
  60.                                     // on ‘synchronize’, so we must recompute  
  61.                                     now = System.currentTimeMillis();  
  62.                                     timeUntilTrigger = triggerTime – now;  
  63.                                     if(timeUntilTrigger >= 1)  
  64.                                         sigLock.wait(timeUntilTrigger);  
  65.                                 } catch (InterruptedException ignore) {  
  66.                                 }  
  67.                             }  
  68.                         }                                 
  69.                         if(releaseIfScheduleChangedSignificantly(trigger, triggerTime)) {  
  70.                             trigger = null;  
  71.                             break;  
  72.                         }  
  73.                         now = System.currentTimeMillis();  
  74.                         timeUntilTrigger = triggerTime – now;  
  75.                        }  
  76.                        if(trigger == null)  
  77.                         continue;  
  78.                          
  79.                        // set trigger to ‘executing’  
  80.                        TriggerFiredBundle bndle = null;  
  81.   
  82.                        boolean goAhead = true;  
  83.                        synchronized(sigLock) {  
  84.                         goAhead = !halted.get();  
  85.                        }  
  86.   
  87.                        if(goAhead) {  
  88.                            try {  
  89.                                bndle = qsRsrcs.getJobStore().triggerFired(ctxt,  
  90.                                        trigger);  
  91.                            } catch (SchedulerException se) {  
  92.                                qs.notifySchedulerListenersError(  
  93.                                        “An error occured while firing trigger ‘”  
  94.                                                + trigger.getFullName() + “‘”, se);  
  95.                            } catch (RuntimeException e) {  
  96.                                getLog().error(  
  97.                                    “RuntimeException while firing trigger ” +  
  98.                                    trigger.getFullName(), e);  
  99.                                // db connection must have failed… keep  
  100.                                // retrying until it’s up…  
  101.                                releaseTriggerRetryLoop(trigger);  
  102.                            }  
  103.                        }  
  104.                          
  105.                        // it’s possible to get ‘null’ if the trigger was paused,  
  106.                        // blocked, or other similar occurrences that prevent it being  
  107.                        // fired at this time…  or if the scheduler was shutdown (halted)  
  108.                        if (bndle == null) {  
  109.                            try {  
  110.                                qsRsrcs.getJobStore().releaseAcquiredTrigger(ctxt,  
  111.                                        trigger);  
  112.                            } catch (SchedulerException se) {  
  113.                                qs.notifySchedulerListenersError(  
  114.                                        “An error occured while releasing trigger ‘”  
  115.                                                + trigger.getFullName() + “‘”, se);  
  116.                                // db connection must have failed… keep retrying  
  117.                                // until it’s up…  
  118.                                releaseTriggerRetryLoop(trigger);  
  119.                            }  
  120.                            continue;  
  121.                        }  
  122.   
  123.                        // TODO: improvements:  
  124.                        //  
  125.                        // 2- make sure we can get a job runshell before firing trigger, or  
  126.                        //   don’t let that throw an exception (right now it never does,  
  127.                        //   but the signature says it can).  
  128.                        // 3- acquire more triggers at a time (based on num threads available?)  
  129.                        JobRunShell shell = null;  
  130.                        try {  
  131.                            shell = qsRsrcs.getJobRunShellFactory().borrowJobRunShell();  
  132.                            shell.initialize(qs, bndle);  
  133.                        } catch (SchedulerException se) {  
  134.                            try {  
  135.                                qsRsrcs.getJobStore().triggeredJobComplete(ctxt,  
  136.                                        trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR);  
  137.                            } catch (SchedulerException se2) {  
  138.                                qs.notifySchedulerListenersError(  
  139.                                        “An error occured while placing job’s triggers in error state ‘”  
  140.                                                + trigger.getFullName() + “‘”, se2);  
  141.                                // db connection must have failed… keep retrying  
  142.                                // until it’s up…  
  143.                                errorTriggerRetryLoop(bndle);  
  144.                            }  
  145.                            continue;  
  146.                        }  
  147.   
  148.                        if (qsRsrcs.getThreadPool().runInThread(shell) == false) {  
  149.                            try {  
  150.                                // this case should never happen, as it is indicative of the  
  151.                                // scheduler being shutdown or a bug in the thread pool or  
  152.                                // a thread pool being used concurrently – which the docs  
  153.                                // say not to do…  
  154.                                getLog().error(“ThreadPool.runInThread() return false!”);  
  155.                                qsRsrcs.getJobStore().triggeredJobComplete(ctxt,  
  156.                                        trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR);  
  157.                            } catch (SchedulerException se2) {  
  158.                                qs.notifySchedulerListenersError(  
  159.                                        “An error occured while placing job’s triggers in error state ‘”  
  160.                                                + trigger.getFullName() + “‘”, se2);  
  161.                                // db connection must have failed… keep retrying  
  162.                                // until it’s up…  
  163.                                releaseTriggerRetryLoop(trigger);  
  164.                            }  
  165.                        }  
  166.                        continue;  
  167.                    }  
  168.                } else { // if(availTreadCount > 0)  
  169.                    continue; // should never happen, if threadPool.blockForAvailableThreads() follows contract  
  170.                }  
  171.   
  172.                long now = System.currentTimeMillis();  
  173.                long waitTime = now + getRandomizedIdleWaitTime();  
  174.                long timeUntilContinue = waitTime – now;  
  175.                synchronized(sigLock) {  
  176.                 try {  
  177.                     sigLock.wait(timeUntilContinue);  
  178.                 } catch (InterruptedException ignore) {  
  179.                 }  
  180.                }  
  181.   
  182.            } catch(RuntimeException re) {  
  183.                getLog().error(“Runtime error occured in main trigger firing loop.”, re);  
  184.            }  
  185.        } // loop…  
  186.   
  187.        // drop references to scheduler stuff to aid garbage collection…  
  188.        qs = null;  
  189.        qsRsrcs = null;  
  190.    }  

    /**

    * QuartzSchedulerThread.run
    * <p>
    * The main processing loop of the <code>QuartzSchedulerThread</code>.
    * </p>
    */
    

    public void run() {

       boolean lastAcquireFailed = false;
       while (!halted.get()) {
           try {
               // check if we're supposed to pause...
               synchronized (sigLock) {
                   while (paused && !halted.get()) {
                       try {
                           // wait until togglePause(false) is called...
                           sigLock.wait(1000L);
                       } catch (InterruptedException ignore) {
                       }
                   }
    
                   if (halted.get()) {
                       break;
                   }
               }
    
               int availTreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();
               if(availTreadCount > 0) { // will always be true, due to semantics of blockForAvailableThreads...
                   Trigger trigger = null;
    
                   long now = System.currentTimeMillis();
                   clearSignaledSchedulingChange();
                   try {
                       trigger = qsRsrcs.getJobStore().acquireNextTrigger(
                               ctxt, now + idleWaitTime);
                       lastAcquireFailed = false;
                   } catch (JobPersistenceException jpe) {
                       if(!lastAcquireFailed) {
                           qs.notifySchedulerListenersError(
                               "An error occured while scanning for the next trigger to fire.",
                               jpe);
                       }
                       lastAcquireFailed = true;
                   } catch (RuntimeException e) {
                       if(!lastAcquireFailed) {
                           getLog().error("quartzSchedulerThreadLoop: RuntimeException "
                                   +e.getMessage(), e);
                       }
                       lastAcquireFailed = true;
                   }
    
                   if (trigger != null) {
                       now = System.currentTimeMillis();
                       long triggerTime = trigger.getNextFireTime().getTime();
                       long timeUntilTrigger = triggerTime - now;
                       while(timeUntilTrigger > 2) {
                           synchronized(sigLock) {
                               if(!isCandidateNewTimeEarlierWithinReason(triggerTime, false)) {
                                   try {
                                       // we could have blocked a long while
                                       // on 'synchronize', so we must recompute
                                       now = System.currentTimeMillis();
                                       timeUntilTrigger = triggerTime - now;
                                       if(timeUntilTrigger >= 1)
                                           sigLock.wait(timeUntilTrigger);
                                   } catch (InterruptedException ignore) {
                                   }
                               }
                           }                               
                           if(releaseIfScheduleChangedSignificantly(trigger, triggerTime)) {
                               trigger = null;
                               break;
                           }
                           now = System.currentTimeMillis();
                           timeUntilTrigger = triggerTime - now;
                       }
                       if(trigger == null)
                           continue;
    
                       // set trigger to 'executing'
                       TriggerFiredBundle bndle = null;
    
                       boolean goAhead = true;
                       synchronized(sigLock) {
                           goAhead = !halted.get();
                       }
    
                       if(goAhead) {
                           try {
                               bndle = qsRsrcs.getJobStore().triggerFired(ctxt,
                                       trigger);
                           } catch (SchedulerException se) {
                               qs.notifySchedulerListenersError(
                                       "An error occured while firing trigger '"
                                               + trigger.getFullName() + "'", se);
                           } catch (RuntimeException e) {
                               getLog().error(
                                   "RuntimeException while firing trigger " +
                                   trigger.getFullName(), e);
                               // db connection must have failed... keep
                               // retrying until it's up...
                               releaseTriggerRetryLoop(trigger);
                           }
                       }
    
                       // it's possible to get 'null' if the trigger was paused,
                       // blocked, or other similar occurrences that prevent it being
                       // fired at this time...  or if the scheduler was shutdown (halted)
                       if (bndle == null) {
                           try {
                               qsRsrcs.getJobStore().releaseAcquiredTrigger(ctxt,
                                       trigger);
                           } catch (SchedulerException se) {
                               qs.notifySchedulerListenersError(
                                       "An error occured while releasing trigger '"
                                               + trigger.getFullName() + "'", se);
                               // db connection must have failed... keep retrying
                               // until it's up...
                               releaseTriggerRetryLoop(trigger);
                           }
                           continue;
                       }
    
                       // TODO: improvements:
                       //
                       // 2- make sure we can get a job runshell before firing trigger, or
                       //   don't let that throw an exception (right now it never does,
                       //   but the signature says it can).
                       // 3- acquire more triggers at a time (based on num threads available?)
                       JobRunShell shell = null;
                       try {
                           shell = qsRsrcs.getJobRunShellFactory().borrowJobRunShell();
                           shell.initialize(qs, bndle);
                       } catch (SchedulerException se) {
                           try {
                               qsRsrcs.getJobStore().triggeredJobComplete(ctxt,
                                       trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR);
                           } catch (SchedulerException se2) {
                               qs.notifySchedulerListenersError(
                                       "An error occured while placing job's triggers in error state '"
                                               + trigger.getFullName() + "'", se2);
                               // db connection must have failed... keep retrying
                               // until it's up...
                               errorTriggerRetryLoop(bndle);
                           }
                           continue;
                       }
    
                       if (qsRsrcs.getThreadPool().runInThread(shell) == false) {
                           try {
                               // this case should never happen, as it is indicative of the
                               // scheduler being shutdown or a bug in the thread pool or
                               // a thread pool being used concurrently - which the docs
                               // say not to do...
                               getLog().error("ThreadPool.runInThread() return false!");
                               qsRsrcs.getJobStore().triggeredJobComplete(ctxt,
                                       trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR);
                           } catch (SchedulerException se2) {
                               qs.notifySchedulerListenersError(
                                       "An error occured while placing job's triggers in error state '"
                                               + trigger.getFullName() + "'", se2);
                               // db connection must have failed... keep retrying
                               // until it's up...
                               releaseTriggerRetryLoop(trigger);
                           }
                       }
                       continue;
                   }
               } else { // if(availTreadCount > 0)
                   continue; // should never happen, if threadPool.blockForAvailableThreads() follows contract
               }
    
               long now = System.currentTimeMillis();
               long waitTime = now + getRandomizedIdleWaitTime();
               long timeUntilContinue = waitTime - now;
               synchronized(sigLock) {
                   try {
                       sigLock.wait(timeUntilContinue);
                   } catch (InterruptedException ignore) {
                   }
               }
    
           } catch(RuntimeException re) {
               getLog().error("Runtime error occured in main trigger firing loop.", re);
           }
       } // loop...
    
       // drop references to scheduler stuff to aid garbage collection...
       qs = null;
       qsRsrcs = null;
    

    }

quartz工作者线程 Quartz
不会在主线程(QuartzSchedulerThread)中处理用户的Job。Quartz
把线程管理的任务委托给ThreadPool。
一般的安装使用org.quartz.simpl.SimpleThreadPool。SimpleThreadPool
创造了必然数额的 WorkerThread 实例来驱动Job可以在线程中展开拍卖。
WorkerThread 是概念在 SimpleThreadPool
类中的内部类,它实质上就是一个线程。 要创立 WorkerThread
的数额以及部署他们的优先级是在文书quartz.properties中并传到工厂。
spring
properties

Java代码
图片 66 图片 67图片 68)

  1. <prop key=”org.quartz.threadPool.class”>org.quartz.simpl.SimpleThreadPool</prop>  
  2. <prop key=”org.quartz.threadPool.threadCount”>20</prop>  
  3. <prop key=”org.quartz.threadPool.threadPriority”>5</prop>  

    org.quartz.simpl.SimpleThreadPool 20 5

主线程(QuartzSchedulerThread)请求ThreadPool去运作
JobRunShell 实例,ThreadPool 就反省看是否有一个可用的劳力线
程。如果所以已布局的劳力线程都是忙的,ThreadPool
就等候直到有一个变为可用。当一个劳力线程是可用的,
并且有一个JobRunShell 等待执行,工作者线程就会调用 JobRunShell 类的
run() 方法。
Quartz 框架允许替换线程池,但必须兑现org.quartz.spi.ThreadPool 接口.
图片 69 图4
quartz内部的主线程和劳引力线程
Quartz的积存和持久化 Quartz 用 JobStores
对 Job、Trigger、calendar 和 Schduler 数据提供一种存储机制。Scheduler
应用已部署的JobStore 来存储和取得到布置信息,并决定正被触发执行的 Job
的天职。 所有的关于哪个 Job
要实践和以什么样时间表来施行他们的音信都来储存在 JobStore。
在 Quartz 中两种可用的 Job 存储类型是:  内存(非持久化) 存储 
持久化存储
JobStore 接口 Quartz 为保有类型的Job存储提供了一个接口。叫
JobStore。所有的Job存储机制,不管是在哪儿或是如何存储他们的信息的,都必须贯彻这些接口。
JobStore 接口的 API 可归纳为下边几类: Job 相关的 API Trigger 相关的 API
Calendar 相关的 API Scheduler 相关的 API
利用内存来存储 Scheduler 音信 Quartz 的内存Job存储类叫做
org.quartz.simple.RAMJobStore,它实现了JobStore 接口的。 RAMJobStore 是
Quartz 的默认的解决方案。 使用这种内存JobStore的功利。
RAMJobStore是安排最简易的
JobStore:默认已经部署好了。见quartz.jar:org.quartz.quartz.properties 
RAMJobStore的进度特别快。所有的 quartz存储操作都在微机内存中
采取持久性的 JobStore 持久性 JobStore = JDBC + 关系型数据库
Quartz 所有的持久化的 JobStore 都扩张自
org.quartz.impl.jdbcjobstore.JobStoreSupport 类。
图片 70
图5 JobStoreSupport 实现了 JobStore 接口,是作为 Quartz
提供的多个具体的持久性 JobStore 类的基类。 Quartz
提供了二种不同品种的JobStoreSupport实现类,每一个统筹为针对一定的数据库环境和配备:
·org.quartz.impl.jdbcjobstore.JobStoreTX
·org.quartz.impl.jdbcjobstore.JobStoreCMT
单身环境中的持久性存储 JobStoreTX 类设计为用于独立环境中。这里的
“独立”,大家是指这样一个条件,在里面不设有与行使容器的政工集成。
#properties配置 org.quartz.jobStore.class =
org.quartz.ompl.jdbcjobstore.JobStoreTX
依靠容器相关的持久性存储 JobStoreCMT
类设计为与程序容器事务集成,容器管理的事物(Container Managed
Transactions (CMT))
crm使用JobStoreTX
因为quart有长日子锁等待情状,不出席系统自身事务(crm任务内工作与quartz本身事务分离).
Quartz 数据库结构
表名描述 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实例使用多套的表,通过改变前缀来实现。
优化 quartz数据表结构 —
1:对首要查询路径字段建立目录

Java代码
图片 71 图片 72图片 73)

  1. create index idx_qrtz_t_next_fire_time on QRTZ_TRIGGERS(NEXT_FIRE_TIME);  
  2. create index idx_qrtz_t_state on QRTZ_TRIGGERS(TRIGGER_STATE);  
  3. create index idx_qrtz_t_nf_st on QRTZ_TRIGGERS(TRIGGER_STATE,NEXT_FIRE_TIME);  
  4. create index idx_qrtz_ft_trig_group on QRTZ_FIRED_TRIGGERS(TRIGGER_GROUP);  
  5. create index idx_qrtz_ft_trig_name on QRTZ_FIRED_TRIGGERS(TRIGGER_NAME);  
  6. create index idx_qrtz_ft_trig_n_g on QRTZ_FIRED_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP);  
  7. create index idx_qrtz_ft_trig_inst_name on QRTZ_FIRED_TRIGGERS(INSTANCE_NAME);  
  8. create index idx_qrtz_ft_job_name on QRTZ_FIRED_TRIGGERS(JOB_NAME);  
  9. create index idx_qrtz_ft_job_group on QRTZ_FIRED_TRIGGERS(JOB_GROUP);  

    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);

— 2:依照Mysql
innodb表结构特征,调整主键,降低二级索引的大小

Java代码
图片 74 图片 75图片 76)

  1. ALTER TABLE QRTZ_TRIGGERS  
  2. ADD UNIQUE KEY IDX_NAME_GROUP(TRIGGER_NAME,TRIGGER_GROUP),  
  3. DROP PRIMARY KEY,  
  4. ADD ID INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,  
  5. ADD PRIMARY KEY (ID);  
  6. ALTER TABLE QRTZ_JOB_DETAILS  
  7. ADD UNIQUE KEY IDX_NAME_GROUP(JOB_NAME,JOB_GROUP),  
  8. DROP PRIMARY KEY,  
  9. ADD ID INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,  
  10. ADD PRIMARY KEY (ID);  

    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集群
只有应用持久的JobStore才能做到Quqrtz集群
图片 77 图6 一个 Quartz
集群中的每个节点是一个独立的 Quartz 应用,它又治本着别样的节点。
需要各自对每个节点分别启动或截止。不像应用服务器的集群,独立的 Quartz
节点并不与另一个节点可能管理节点通信。 Quartz
应用是通过数据库表来感知到另一接纳。
布置集群

Xml代码
图片 78 图片 79图片 80)

  1. <prop key=”org.quartz.jobStore.class”>org.quartz.impl.jdbcjobstore.JobStoreTX</prop>  
  2. <!– 集群配置 –>  
  3. <prop key=”org.quartz.jobStore.isClustered”>true</prop>  
  4. <prop key=”org.quartz.jobStore.clusterCheckinInterval”>15000</prop>  
  5. <prop key=”org.quartz.jobStore.maxMisfiresToHandleAtATime”>1</prop>  
  6. <!– 数据源配置 使用DBCP连接池 数据源与dataSource一致 –>  
  7. <prop key=”org.quartz.jobStore.dataSource”>myDS</prop>  
  8. <prop key=”org.quartz.dataSource.myDS.driver”>${database.driverClassName}</prop>  
  9. <prop key=”org.quartz.dataSource.myDS.URL”>${database.url}</prop>  
  10. <prop key=”org.quartz.dataSource.myDS.user”>${database.username}</prop>  
  11. <prop key=”org.quartz.dataSource.myDS.password”>${database.password}</prop>  
  12. <prop key=”org.quartz.dataSource.myDS.maxConnections”>5</prop>  

    org.quartz.impl.jdbcjobstore.JobStoreTX true 15000 1 myDS ${database.driverClassName} ${database.url} ${database.username} ${database.password} 5

org.quartz.jobStore.class 属性为 JobStoreTX,
将任务持久化到多少中。因为集群中节点倚重于数据库来传播Scheduler实例的情景,你只可以在利用
JDBC JobStore 时利用 Quartz 集群。
org.quartz.jobStore.isClustered 属性为
true,通知Scheduler实例要它参预到一个集群当中。
org.quartz.jobStore.clusterCheckinInterval
特性定义了Scheduler 实例检入到数据库中的频率(单位:毫秒)。 Scheduler
检查是否其他的实例到了它们应当检入的时候未检入; 这能提议一个难倒的
Scheduler 实例,且当前 Scheduler 会以此来接管任何履行破产并可复原的
Job。 通过检入操作,Scheduler
也会更新自己的情景记录。clusterChedkinInterval 越小,Scheduler
节点检查失利的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)
集群实现分析 Quartz原来码分析: 基于数据库表锁实现多Quartz_Node
对Job,Trigger,Calendar等共同机制

Sql代码
图片 81 图片 82图片 83)

  1. — 数据库锁定表  
  2. CREATE TABLE `QRTZ_LOCKS` (  
  3.   `LOCK_NAME` varchar(40) NOT NULL,  
  4.   PRIMARY KEY (`LOCK_NAME`)  
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  6. — 记录  
  7. +—————–+  
  8. | LOCK_NAME       |  
  9. +—————–+  
  10. | CALENDAR_ACCESS |   
  11. | JOB_ACCESS      |   
  12. | MISFIRE_ACCESS  |   
  13. | STATE_ACCESS    |   
  14. | TRIGGER_ACCESS  |   
  15. +—————–+  

    — 数据库锁定表
    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 |
    +—————–+

经过行级别锁实现多节点处理

Java代码
图片 84 图片 85图片 86)

  1. /** 
  2.  * Internal database based lock handler for providing thread/resource locking  
  3.  * in order to protect resources from being altered by multiple threads at the  
  4.  * same time. 
  5.  *  
  6.  * @author jhouse 
  7.  */  
  8. public class StdRowLockSemaphore extends DBSemaphore {  
  9.   
  10.     /* 
  11.      * Constants. 
  12.      * 锁定SQL语句 
  13.      *  
  14.      */  
  15.     public static final String SELECT_FOR_LOCK = “SELECT * FROM ”  
  16.             + TABLE_PREFIX_SUBST + TABLE_LOCKS + ” WHERE ” + COL_LOCK_NAME  
  17.             + ” = ? FOR UPDATE”;  
  18.   
  19.     /** 
  20.      * This constructor is for using the <code>StdRowLockSemaphore</code> as 
  21.      * a bean. 
  22.      */  
  23.     public StdRowLockSemaphore() {  
  24.         super(DEFAULT_TABLE_PREFIX, null, SELECT_FOR_LOCK);  
  25.     }  
  26.   
  27.     public StdRowLockSemaphore(String tablePrefix, String seletWithLockSQL) {  
  28.         super(tablePrefix, selectWithLockSQL, SELECT_FOR_LOCK);  
  29.     }  
  30.   
  31.     /** 
  32.      * Execute the SQL select for update that will lock the proper database row. 
  33.      * 指定锁定SQL 
  34.      */  
  35.     protected void executeSQL(Connection conn, String lockName, String expandedSQL) throws LockException {  
  36.         PreparedStatement ps = null;  
  37.         ResultSet rs = null;  
  38.         try {  
  39.             ps = conn.prepareStatement(expandedSQL);  
  40.             ps.setString(1, lockName);  
  41.   
  42.             if (getLog().isDebugEnabled()) {  
  43.                 getLog().debug(  
  44.                     “Lock ‘” + lockName + “‘ is being obtained: ” +   
  45.                     Thread.currentThread().getName());  
  46.             }  
  47.             rs = ps.executeQuery();  
  48.             if (!rs.next()) {  
  49.                 throw new SQLException(Util.rtp(  
  50.                     “No row exists in table ” + TABLE_PREFIX_SUBST +   
  51.                     TABLE_LOCKS + ” for lock named: ” + lockName, getTablePrefix()));  
  52.             }  
  53.         } catch (SQLException sqle) {  
  54.             if (getLog().isDebugEnabled()) {  
  55.                 getLog().debug(  
  56.                     “Lock ‘” + lockName + “‘ was not obtained by: ” +   
  57.                     Thread.currentThread().getName());  
  58.             }  
  59.             throw new LockException(“Failure obtaining db row lock: ”  
  60.                     + sqle.getMessage(), sqle);  
  61.         } finally {  
  62.             if (rs != null) {   
  63.                 try {  
  64.                     rs.close();  
  65.                 } catch (Exception ignore) {  
  66.                 }  
  67.             }  
  68.             if (ps != null) {  
  69.                 try {  
  70.                     ps.close();  
  71.                 } catch (Exception ignore) {  
  72.                 }  
  73.             }  
  74.         }  
  75.     }  
  76.   
  77.     protected String getSelectWithLockSQL() {  
  78.         return getSQL();  
  79.     }  
  80.   
  81.     public void setSelectWithLockSQL(String selectWithLockSQL) {  
  82.         setSQL(selectWithLockSQL);  
  83.     }  
  84. }  
  85.   
  86.    /** 
  87.      * Grants a lock on the identified resource to the calling thread (blocking 
  88.      * until it is available). 
  89.      * 获取QRTZ_LOCKS行级锁 
  90.      * @return true if the lock was obtained. 
  91.      */  
  92.     public boolean obtainLock(Connection conn, String lockName) throws LockException {  
  93.         lockName = lockName.intern();  
  94.   
  95.         Logger log = getLog();  
  96.   
  97.         if(log.isDebugEnabled()) {  
  98.             log.debug(  
  99.                 “Lock ‘” + lockName + “‘ is desired by: ”  
  100.                         + Thread.currentThread().getName());  
  101.         }  
  102.         if (!isLockOwner(conn, lockName)) {  
  103.             executeSQL(conn, lockName, expandedSQL);  
  104.               
  105.             if(log.isDebugEnabled()) {  
  106.                 log.debug(  
  107.                     “Lock ‘” + lockName + “‘ given to: ”  
  108.                             + Thread.currentThread().getName());  
  109.             }  
  110.             getThreadLocks().add(lockName);  
  111.             //getThreadLocksObtainer().put(lockName, new  
  112.             // Exception(“Obtainer…”));  
  113.         } else if(log.isDebugEnabled()) {  
  114.             log.debug(  
  115.                 “Lock ‘” + lockName + “‘ Is already owned by: ”  
  116.                         + Thread.currentThread().getName());  
  117.         }  
  118.         return true;  
  119.     }  
  120.   
  121.     /** 
  122.      * Release the lock on the identified resource if it is held by the calling thread. 
  123.      * 释放QRTZ_LOCKS行级锁 
  124.      */  
  125.     public void releaseLock(Connection conn, String lockName) {  
  126.         lockName = lockName.intern();  
  127.   
  128.         if (isLockOwner(conn, lockName)) {  
  129.             if(getLog().isDebugEnabled()) {  
  130.                 getLog().debug(  
  131.                     “Lock ‘” + lockName + “‘ returned by: ”  
  132.                             + Thread.currentThread().getName());  
  133.             }  
  134.             getThreadLocks().remove(lockName);  
  135.             //getThreadLocksObtainer().remove(lockName);  
  136.         } else if (getLog().isDebugEnabled()) {  
  137.             getLog().warn(  
  138.                 “Lock ‘” + lockName + “‘ attempt to return by: ”  
  139.                         + Thread.currentThread().getName()  
  140.                         + ” — but not owner!”,  
  141.                 new Exception(“stack-trace of wrongful returner”));  
  142.         }  
  143.     }  

    /**

    • Internal database based lock handler for providing thread/resource locking
    • in order to protect resources from being altered by multiple threads at the
    • same time.
    • @author jhouse
      */
      public class StdRowLockSemaphore extends DBSemaphore {

      /*

      • Constants.
      • 锁定SQL语句
      • /
        public static final String SELECT_FOR_LOCK = “SELECT
        FROM “

         + TABLE_PREFIX_SUBST + TABLE_LOCKS + " WHERE " + COL_LOCK_NAME
         + " = ? FOR UPDATE";
        

        /**

      • This constructor is for using the StdRowLockSemaphore as
      • a bean.
        */
        public StdRowLockSemaphore() {
        super(DEFAULT_TABLE_PREFIX, null, SELECT_FOR_LOCK);
        }

        public StdRowLockSemaphore(String tablePrefix, String seletWithLockSQL) {
        super(tablePrefix, selectWithLockSQL, SELECT_FOR_LOCK);
        }

        /**

      • Execute the SQL select for update that will lock the proper database row.
      • 点名锁定SQL
        */
        protected void executeSQL(Connection conn, String lockName, String expandedSQL) throws LockException {
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {

         ps = conn.prepareStatement(expandedSQL);
         ps.setString(1, lockName);
        
         if (getLog().isDebugEnabled()) {
             getLog().debug(
                 "Lock '" + lockName + "' is being obtained: " + 
                 Thread.currentThread().getName());
         }
         rs = ps.executeQuery();
         if (!rs.next()) {
             throw new SQLException(Util.rtp(
                 "No row exists in table " + TABLE_PREFIX_SUBST + 
                 TABLE_LOCKS + " for lock named: " + lockName, getTablePrefix()));
         }
        

        } catch (SQLException sqle) {

         if (getLog().isDebugEnabled()) {
             getLog().debug(
                 "Lock '" + lockName + "' was not obtained by: " + 
                 Thread.currentThread().getName());
         }
         throw new LockException("Failure obtaining db row lock: "
                 + sqle.getMessage(), sqle);
        

        } finally {

         if (rs != null) { 
             try {
                 rs.close();
             } catch (Exception ignore) {
             }
         }
         if (ps != null) {
             try {
                 ps.close();
             } catch (Exception ignore) {
             }
         }
        

        }
        }

        protected String getSelectWithLockSQL() {
        return getSQL();
        }

        public void setSelectWithLockSQL(String selectWithLockSQL) {
        setSQL(selectWithLockSQL);
        }
        }

      /**

      • Grants a lock on the identified resource to the calling thread (blocking
      • until it is available).
      • 获取QRTZ_LOCKS行级锁
      • @return true if the lock was obtained.
        */
        public boolean obtainLock(Connection conn, String lockName) throws LockException {
        lockName = lockName.intern();

        Logger log = getLog();

        if(log.isDebugEnabled()) {

         log.debug(
             "Lock '" + lockName + "' is desired by: "
                     + Thread.currentThread().getName());
        

        }
        if (!isLockOwner(conn, lockName)) {

         executeSQL(conn, lockName, expandedSQL);
        
         if(log.isDebugEnabled()) {
             log.debug(
                 "Lock '" + lockName + "' given to: "
                         + Thread.currentThread().getName());
         }
         getThreadLocks().add(lockName);
         //getThreadLocksObtainer().put(lockName, new
         // Exception("Obtainer..."));
        

        } else if(log.isDebugEnabled()) {

         log.debug(
             "Lock '" + lockName + "' Is already owned by: "
                     + Thread.currentThread().getName());
        

        }
        return true;
        }

        /**

      • Release the lock on the identified resource if it is held by the calling thread.
      • 释放QRTZ_LOCKS行级锁
        */
        public void releaseLock(Connection conn, String lockName) {
        lockName = lockName.intern();

        if (isLockOwner(conn, lockName)) {

         if(getLog().isDebugEnabled()) {
             getLog().debug(
                 "Lock '" + lockName + "' returned by: "
                         + Thread.currentThread().getName());
         }
         getThreadLocks().remove(lockName);
         //getThreadLocksObtainer().remove(lockName);
        

        } else if (getLog().isDebugEnabled()) {

         getLog().warn(
             "Lock '" + lockName + "' attempt to return by: "
                     + Thread.currentThread().getName()
                     + " -- but not owner!",
             new Exception("stack-trace of wrongful returner"));
        

        }
        }

JobStoreTX
控制并发代码

Java代码
图片 87 图片 88图片 89)

  1. /** 
  2.  * Execute the given callback having optionally aquired the given lock. 
  3.  * For <code>JobStoreTX</code>, because it manages its own transactions 
  4.  * and only has the one datasource, this is the same behavior as  
  5.  * executeInNonManagedTXLock().  
  6.  * @param lockName The name of the lock to aquire, for example  
  7.  * “TRIGGER_ACCESS”.  If null, then no lock is aquired, but the 
  8.  * lockCallback is still executed in a transaction. 
  9.  *  
  10.  * @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback) 
  11.     * @see JobStoreCMT#executeInLock(String, TransactionCallback) 
  12.     * @see JobStoreSupport#getNonManagedTXConnection() 
  13.     * @see JobStoreSupport#getConnection() 
  14. */  
  15.    protected Object executeInLock(String lockName, TransactionCallback txCallback) throws JobPersistenceException {  
  16.        return executeInNonManagedTXLock(lockName, txCallback);  
  17.    }  
  18.   
  19. 使用JobStoreSupport.executeInNonManagedTXLock 实现:  
  20. /** 
  21.     * Execute the given callback having optionally aquired the given lock. 
  22.     * This uses the non-managed transaction connection. 
  23.     *  
  24.     * @param lockName The name of the lock to aquire, for example  
  25.     * “TRIGGER_ACCESS”.  If null, then no lock is aquired, but the 
  26.     * lockCallback is still executed in a non-managed transaction.  
  27.     */  
  28.    protected Object executeInNonManagedTXLock(  
  29.            String lockName,   
  30.            TransactionCallback txCallback) throws JobPersistenceException {  
  31.        boolean transOwner = false;  
  32.        Connection conn = null;  
  33.        try {  
  34.            if (lockName != null) {  
  35.                // If we aren’t using db locks, then delay getting DB connection   
  36.                // until after acquiring the lock since it isn’t needed.  
  37.                if (getLockHandler().requiresConnection()) {  
  38.                    conn = getNonManagedTXConnection();  
  39.                }  
  40.             //获取锁  
  41.                transOwner = getLockHandler().obtainLock(conn, lockName);  
  42.            }  
  43.            if (conn == null) {  
  44.                conn = getNonManagedTXConnection();  
  45.            }  
  46.             //回调需要履行的sql语句如:(更新Trigger为运行中(ACQUIRED),删除执行过的Trigger等)  
  47.            Object result = txCallback.execute(conn);  
  48.         //JobStoreTX自身保障工作  
  49.            commitConnection(conn);  
  50.            Long sigTime = clearAndGetSignalSchedulingChangeOnTxCompletion();  
  51.            if(sigTime != null && sigTime >= 0) {  
  52.                signalSchedulingChangeImmediately(sigTime);  
  53.            }  
  54.            return result;  
  55.        } catch (JobPersistenceException e) {  
  56.            rollbackConnection(conn);  
  57.            throw e;  
  58.        } catch (RuntimeException e) {  
  59.            rollbackConnection(conn);  
  60.            throw new JobPersistenceException(“Unexpected runtime exception: ” + e.getMessage(), e);  
  61.        } finally {  
  62.            try {  
  63.             //释放锁  
  64.                releaseLock(conn, lockName, transOwner);  
  65.            } finally {  
  66.                cleanupConnection(conn);  
  67.            }  
  68.        }  
  69.    }  

    /**

     * Execute the given callback having optionally aquired the given lock.
     * For <code>JobStoreTX</code>, because it manages its own transactions
     * and only has the one datasource, this is the same behavior as 
     * executeInNonManagedTXLock(). 
     * @param lockName The name of the lock to aquire, for example 
     * "TRIGGER_ACCESS".  If null, then no lock is aquired, but the
     * lockCallback is still executed in a transaction.
     * 
     * @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback)
    * @see JobStoreCMT#executeInLock(String, TransactionCallback)
    * @see JobStoreSupport#getNonManagedTXConnection()
    * @see JobStoreSupport#getConnection()
    */
    protected Object executeInLock(String lockName, TransactionCallback txCallback) throws JobPersistenceException {
        return executeInNonManagedTXLock(lockName, txCallback);
    }
    
    使用JobStoreSupport.executeInNonManagedTXLock 实现:
    /**
     * Execute the given callback having optionally aquired the given lock.
     * This uses the non-managed transaction connection.
     * 
     * @param lockName The name of the lock to aquire, for example 
     * "TRIGGER_ACCESS".  If null, then no lock is aquired, but the
     * lockCallback is still executed in a non-managed transaction. 
     */
    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);
            }
        }
    }
    

JobStoreCMT
控制并发代码

Java代码
图片 90 图片 91图片 92)

  1. /** 
  2.     * Execute the given callback having optionally acquired the given lock.   
  3.     * Because CMT assumes that the connection is already part of a managed 
  4.     * transaction, it does not attempt to commit or rollback the  
  5.     * enclosing transaction. 
  6.     *  
  7.     * @param lockName The name of the lock to acquire, for example  
  8.     * “TRIGGER_ACCESS”.  If null, then no lock is acquired, but the 
  9.     * txCallback is still executed in a transaction. 
  10.     *  
  11.     * @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback) 
  12.     * @see JobStoreTX#executeInLock(String, TransactionCallback) 
  13.     * @see JobStoreSupport#getNonManagedTXConnection() 
  14.     * @see JobStoreSupport#getConnection() 
  15.     */  
  16.   
  17.    protected Object executeInLock(String lockName, TransactionCallback txCallback) throws JobPersistenceException {  
  18.        boolean transOwner = false;  
  19.        Connection conn = null;  
  20.        try {  
  21.            if (lockName != null) {  
  22.                // If we aren’t using db locks, then delay getting DB connection   
  23.                // until after acquiring the lock since it isn’t needed.  
  24.                if (getLockHandler().requiresConnection()) {  
  25.                    conn = getConnection();  
  26.                }  
  27.                transOwner = getLockHandler().obtainLock(conn, lockName);  
  28.            }  
  29.   
  30.            if (conn == null) {  
  31.                conn = getConnection();  
  32.            }  
  33.         //没有工作提交操作,与任务共享一个政工  
  34.            return txCallback.execute(conn);  
  35.        } finally {  
  36.            try {  
  37.                releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner);  
  38.            } finally {  
  39.                cleanupConnection(conn);  
  40.            }  
  41.        }  
  42.    }  

    /**

     * Execute the given callback having optionally acquired the given lock.  
     * Because CMT assumes that the connection is already part of a managed
     * transaction, it does not attempt to commit or rollback the 
     * enclosing transaction.
     * 
     * @param lockName The name of the lock to acquire, for example 
     * "TRIGGER_ACCESS".  If null, then no lock is acquired, but the
     * txCallback is still executed in a transaction.
     * 
     * @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback)
     * @see JobStoreTX#executeInLock(String, TransactionCallback)
     * @see JobStoreSupport#getNonManagedTXConnection()
     * @see JobStoreSupport#getConnection()
     */
    
    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);
            }
        }
    }
    

CRM中quartz与Spring结合使用 Spring
通过提供org.springframework.scheduling.quartz下的封装类对quartz扶助可是现阶段存在问题  1:Spring3.0最近不补助Quartz2.x之上版本
Caused by: java.lang.IncompatibleClassChangeError: class
org.springframework.scheduling.quartz.CronTriggerBean has interface
org.quartz.CronTrigger as super class 原因是
org.quartz.CronTrigger在2.0从class变成了一个interface造成IncompatibleClassChangeError错误。
釜底抽薪:无解,要想行使spring和quartz结合的章程 只好使用Quartz1.x本子。
2:org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean报 
java.io.NotSerializableException分外,需要团结实现QuartzJobBean。
解决:spring
bug己经在http://jira.springframework.org/browse/SPR-3797找到解决方案,
作者重写了MethodInvokingJobDetailFactoryBean.
3:Spring内bean必须要兑现序列化接口,否则不可以透过Sprng
属性注入的点子为job提供工作对象
解决:

Java代码
图片 93 图片 94图片 95)

  1. //使用可体系化工具类获取Spring容器对象  
  2. @Service(“springBeanService”)  
  3. public class SpringBeanService implements Serializable{private static final long serialVersionUID = -2228376078979553838L;  
  4.     public <T> T getBean(Class<T> clazz,String beanName){  
  5.         ApplicationContext context = ContextLoader.getCurrentWebApplicationContext();  
  6.         return (T)context.getBean(beanName);  
  7.     }  
  8. }  

    //使用可体系化工具类获取Spring容器对象
    @瑟维斯(“springBean瑟维斯(Service)”)
    public class SpringBean瑟维斯(Service) implements Serializable{private static final long serialVersionUID = -2228376078979553838L;

    public <T> T getBean(Class<T> clazz,String beanName){
        ApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
        return (T)context.getBean(beanName);
    }
    

    }

CRM中quartz模块部分代码
1:定义所有job的父类,并承担老大发送邮件任务和日志任务

Java代码
图片 96 图片 97图片 98)

  1. public abstract class BaseQuartzJob implements Job, Serializable {  
  2.     private static final long serialVersionUID = 3347549365534415931L;  
  3.     private Logger logger = LoggerFactory.getLogger(this.getClass());  
  4.       
  5.     //定义抽象方法,供子类实现  
  6.     public abstract void action(JobExecutionContext context);  
  7.       
  8.     @Override  
  9.     public void execute(JobExecutionContext context) throws JobExecutionException {  
  10.         try {  
  11.             long start = System.currentTimeMillis();  
  12.             this.action(context);  
  13.             long end = System.currentTimeMillis();  
  14.             JobDetail jobDetail = context.getJobDetail();  
  15.             Trigger trigger = context.getTrigger();  
  16.             StringBuilder buffer = new StringBuilder();  
  17.             buffer.append(“jobName = “).append(jobDetail.getName()).append(” triggerName = “)  
  18.             .append(trigger.getName()).append(” 执行到位 , 耗时: “).append((end – start)).append(” ms”);  
  19.             logger.info(buffer.toString());  
  20.         } catch (Exception e) {  
  21.             doResolveException(context != null ? context.getMergedJobDataMap() : null, e);  
  22.         }  
  23.     }  
  24.     @SuppressWarnings(“unchecked”)  
  25.     private void doResolveException(JobDataMap dataMap, Exception ex) {  
  26.         //发送邮件实现此处省略  
  27.         //…  
  28.     }  
  29. }  

    public abstract class BaseQuartzJob implements Job, Serializable {

    private static final long serialVersionUID = 3347549365534415931L;
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    //定义抽象方法,供子类实现
    public abstract void action(JobExecutionContext context);
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        try {
            long start = System.currentTimeMillis();
            this.action(context);
            long end = System.currentTimeMillis();
            JobDetail jobDetail = context.getJobDetail();
            Trigger trigger = context.getTrigger();
            StringBuilder buffer = new StringBuilder();
            buffer.append("jobName = ").append(jobDetail.getName()).append(" triggerName = ")
            .append(trigger.getName()).append(" 执行完成 , 耗时: ").append((end - start)).append(" ms");
            logger.info(buffer.toString());
        } catch (Exception e) {
            doResolveException(context != null ? context.getMergedJobDataMap() : null, e);
        }
    }
    @SuppressWarnings("unchecked")
    private void doResolveException(JobDataMap dataMap, Exception ex) {
        //发送邮件实现此处省略
        //...
    }
    

    }

2:抽象Quartz操作接口(实现类 toSee:
QuartzServiceImpl)

Java代码
图片 99 图片 100图片 101)

  1. /** 
  2.  * 
  3.  * @author zhangyijun 
  4.  * @created 2012-10-22 
  5.  * 
  6.  * @version 1.0 
  7.  */  
  8. @Service  
  9. public interface QuartzService {  
  10. /** 
  11.  * 获取具有trigger 
  12.  * @param page 
  13.  * @param orderName 
  14.  * @param sortType 
  15.  * @return 
  16.  */  
  17.  List<Map<String, Object>> getQrtzTriggers(Page page, String orderName, String sortType);  
  18. /** 
  19.  * 获取具有jobDetail 
  20.  * 
  21.  * @return 
  22.  */  
  23.  List<Map<String, Object>> getQrtzJobDetails();  
  24. /** 
  25.  * 执行Trigger操作 
  26.  * 
  27.  * @param name 
  28.  * @param group 
  29.  * @param action 
  30.  * <br/> 
  31.  */  
  32.  void executeTriggerAction(String name, String group, Integer action);  
  33. /** 
  34.  * 执行JobDetail操作 
  35.  * 
  36.  * @param name 
  37.  * @param group 
  38.  * @param action 
  39.  * <br/> 
  40.  */  
  41.  void executeJobAction(String name, String group, Integer action);  
  42. /** 
  43.  * 动态添加trigger 
  44.  * 
  45.  * @param jobName 
  46.  * @param jobGroup 
  47.  * @param triggerBean 
  48.  */  
  49.  void addTrigger(String jobName, String jobGroup, TriggerViewBean triggerBean);  
  50. /** 
  51.  * 定时举办任务 
  52.  * 
  53.  * @param jobDetail 
  54.  * @param data 
  55.  */  
  56.   
  57.  void addTriggerForDate(JobDetail jobDetail, String triggerName , String  
  58.  triggerGroup , Date date, Map<String, Object> triggerDataMap) ;  
  59. /** 
  60.  * 获取分布式Scheduler列表 
  61.  * 
  62.  * @return 
  63.  */  
  64.  List<Map<String, Object>> getSchedulers();  
  65. /** 
  66.  * 获取触发器 
  67.  * @param name 
  68.  * @param group 
  69.  * @return 
  70.  */  
  71.  public Trigger getTrigger(String name, String group);  
  72. /** 
  73.  * 获取JobDetail 
  74.  * @param name 
  75.  * @param group 
  76.  * @return 
  77.  */  
  78.  public JobDetail getJobDetail(String name, String group);  
  79. }  

    /*

    • @author zhangyijun
    • @created 2012-10-22
      *
    • @version 1.0
      */
      @Service
      public interface QuartzService {
      /**
    • 赢得具有trigger
    • @param page
    • @param orderName
    • @param sortType
    • @return
      */
      List> getQrtzTriggers(Page page, String orderName, String sortType);
      /**
    • 获取具有jobDetail
      *
    • @return
      */
      List> getQrtzJobDetails();
      /**
    • 执行Trigger操作
      *
    • @param name
    • @param group
    • @param action
    • */
      void executeTriggerAction(String name, String group, Integer action);
      /**
    • 执行JobDetail操作
      *
    • @param name
    • @param group
    • @param action
    • */
      void executeJobAction(String name, String group, Integer action);
      /**
    • 动态添加trigger
      *
    • @param jobName
    • @param jobGroup
    • @param triggerBean
      */
      void addTrigger(String jobName, String jobGroup, TriggerViewBean triggerBean);
      /**
    • 定时执行任务
      *
    • @param jobDetail
    • @param data
      */

      void addTriggerForDate(JobDetail jobDetail, String triggerName , String
      triggerGroup , Date date, Map triggerDataMap) ;
      /**

    • 得到分布式Scheduler列表
      *
    • @return
      */
      List> getSchedulers();
      /**
    • 得到触发器
    • @param name
    • @param group
    • @return
      */
      public Trigger getTrigger(String name, String group);
      /**
    • 获取JobDetail
    • @param name
    • @param group
    • @return
      */
      public JobDetail getJobDetail(String name, String group);
      }

3:在Spring配置job,trigger,Scheduler,Listener组件

Xml代码
图片 102 图片 103图片 104)

  1. <!– 扫描公司状态创制定时任务 –>  
  2. <bean id=”accountStatusTaskScannerJobDetail”  
  3.  class=”org.springframework.scheduling.quartz.JobDetailBean”>  
  4.     <property name=”name” value=”accountStatusTaskScannerJobDetail”></property>  
  5.     <property name=”group” value=”CrmAccountGroup”></property>  
  6.     <property name=”jobClass” value=”***.crm.quartz.job.AccountStatusTaskScannerJob”></property>  
  7.     <!– requestsRecovery属性为true,则当Quartz服务被暂停后,再一次启航任务时会尝试复苏执行此前未形成的有所任务–>  
  8.     <property name=”requestsRecovery” value=”true”/>  
  9.     <!– 标识job是从头到尾的,删除所有触发器的时候不被删除 –>  
  10.     <property name=”durability” value=”true”/>  
  11.     <property name=”volatility” value=”false”></property>  
  12. </bean>  
  13. <bean id=”accountStatusTaskScannerTrigger” class=”org.springframework.scheduling.quartz.CronTriggerBean”>  
  14.      <property name=”group” value=”CrmDealGroup”></property>  
  15.      <property name=”name” value=”accountStatusTaskScannerTrigger”></property>  
  16.     <property name=”jobDetail” ref=”accountStatusTaskScannerJobDetail”></property>  
  17.     <property name=”cronExpression” value=”0 0 1 * * ?”></property>  
  18. </bean>  
  19.   
  20. <!– 定义Quartz 监听器 –>  
  21. <bean id=”quartzExceptionSchedulerListener”   
  22. class=”***.crm.quartz.listener.QuartzExceptionSchedulerListener”></bean>  
  23.   
  24. <!– Quartz调度工厂 –>  
  25. <bean id=”quartzScheduler”  
  26.  class=”org.springframework.scheduling.quartz.SchedulerFactoryBean”>  
  27.     <property name=”quartzProperties”>  
  28.     <props>  
  29.         <prop key=”org.quartz.scheduler.instanceName”>CRMscheduler</prop>  
  30.         <prop key=”org.quartz.scheduler.instanceId”>AUTO</prop>  
  31.         <!– 线程池配置 –>  
  32.         <prop key=”org.quartz.threadPool.class”>org.quartz.simpl.SimpleThreadPool</prop>  
  33.         <prop key=”org.quartz.threadPool.threadCount”>20</prop>  
  34.         <prop key=”org.quartz.threadPool.threadPriority”>5</prop>  
  35.         <!– JobStore 配置 –>  
  36.         <prop key=”org.quartz.jobStore.class”>org.quartz.impl.jdbcjobstore.JobStoreTX</prop>  
  37.         <!– 集群配置 –>  
  38.         <prop key=”org.quartz.jobStore.isClustered”>false</prop>  
  39.         <prop key=”org.quartz.jobStore.clusterCheckinInterval”>15000</prop>  
  40.         <prop key=”org.quartz.jobStore.maxMisfiresToHandleAtATime”>1</prop>  
  41.         <!– 数据源配置 使用DBCP连接池 数据源与dataSource一致 –>  
  42.         <prop key=”org.quartz.jobStore.dataSource”>myDS</prop>  
  43.         <prop key=”org.quartz.dataSource.myDS.driver”>${database.driverClassName}</prop>  
  44.         <prop key=”org.quartz.dataSource.myDS.URL”>${database.url}</prop>  
  45.         <prop key=”org.quartz.dataSource.myDS.user”>${database.username}</prop>  
  46.         <prop key=”org.quartz.dataSource.myDS.password”>${database.password}</prop>  
  47.         <prop key=”org.quartz.dataSource.myDS.maxConnections”>5</prop>  
  48.         <prop key=”org.quartz.jobStore.misfireThreshold”>120000</prop>  
  49.     </props>  
  50.     </property>  
  51.     <property name=”schedulerName” value=”CRMscheduler” />  
  52.     <!–必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再起步–>  
  53.     <property name=”startupDelay” value=”30″/>  
  54.     <property name=”applicationContextSchedulerContextKey” value=”applicationContextKey” />  
  55.     <!–可选,QuartzScheduler 启动时更新己存在的Job,这样就无须每趟修改targetObject后删除qrtz_job_details表对应记录了 –>  
  56.     <property name=”overwriteExistingJobs” value=”true” />  
  57.     <!– 设置自动启动 –>  
  58.     <property name=”autoStartup” value=”true” />  
  59.     <!– 注册触发器 –>  
  60.     <property name=”triggers”>  
  61.  <list>  
  62.     <ref bean=”dailyStatisticsTrigger” />  
  63.     <ref bean=”accountGrabedScannerTrigger” />  
  64.     <ref bean=”syncAccountFromPOITrigger” />  
  65.     <ref bean=”userSyncScannerTrigger” />  
  66.     <ref bean=”syncParentBranchFromPOITrigger”/>  
  67.     <ref bean=”privateReminderTrigger” />  
  68.     <ref bean=”onlineBranchesScannerTrigger” />  
  69.     <ref bean=”syncCtContactServiceTrigger” />  
  70.     <ref bean=”dealLinkDianpingScannerTrigger” />  
  71.     <ref bean=”accountStatusTaskScannerTrigger”/>  
  72.     <ref bean=”nDaysActivityScannerTrigger”/>  
  73.  </list>  
  74.  </property>  
  75. <!– 注册jobDetail –>  
  76.  <property name=”jobDetails”>  
  77.     <list>  
  78.         <ref bean=”myTestQuartzJobDetail”/>  
  79.         <ref bean=”accountPrivateToProtectedJobDetail”/>  
  80.         <ref bean=”accountProtectedToPublicJobDetail”/>  
  81.  <ref bean=”nDaysActivityToProtectedJobDetail”/>  
  82.  </list>  
  83.  </property>  
  84. <property name=”schedulerListeners”>  
  85.     <list>  
  86.         <ref bean=”quartzExceptionSchedulerListener”/>  
  87.     </list>  
  88.  </property>  
  89. </bean>  


    CRMscheduler AUTO org.quartz.simpl.SimpleThreadPool 20 5 org.quartz.impl.jdbcjobstore.JobStoreTX false 15000 1 myDS ${database.driverClassName} ${database.url} ${database.username} ${database.password} 5 120000












Crm最近可以做到对Quartz实例的督察,操作.动态部署Trigger
图片 105
图片 106
图片 107
图片 108
连续待开发功用和问题
1:如今落实对job,Trigger操作,动态部署Trigger,后续需要参加Calendar(排除特定日期),Listener(动态加载监控),Job的动态部署(只要bean的称号和措施名,就可完成对job生成,部署)
2:由于Quartz集群中的job如今是在肆意一台server中施行,Quartz日志生成各自的系统目录中,
quartz日志不能统一.
3:Quartz2.x早就补助可选节点执行job(期待Spring升级后对新Quartz襄助)
4:Quartz内部的DB操作大量Trigger存在严重竞争问题,弹指间大气trigger执行,如今不得不通过(org.quartz.jobStore.tablePrefix
= QRTZ)分表操作,存在长日子lock_wait(新本子据说有增强);
5:假如有需要,可以抽取出Quartz,变成单独的劳动,供其他系统调度使用使用

相关文章