Quartz集群

干什么选Quartz:
1)资历够老,创立于1998年,比struts1还早,但是一直于创新(27 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
介绍了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 Scheduler 是 Quartz
的显要 API。与Quartz大部分相是起给 Scheduler
之上的。客服端与Scheduler 交互是通过org.quartz.Scheduler接口。
Scheduler的兑现:对法调用会传递及 QuartzScheduler
实例上。QuartzScheduler
对于客户端是不可见的,并且为不设有和这个实例的直接互动。

图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代码
 )

  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代码
 )

  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代码
 )

  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代码
 )

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

    //Start the scheduler
    scheduler.start();

start()
方法让调用,Scheduler 就起搜需要执行的 Job。在公碰巧获得一个 Scheduler
新的实例时,或者 Scheduler 被装也 standby 模式后,你才可以调用 start()
方法。

Java代码
 )

  1. public void standby() throws SchedulerException;  

    public void standby() throws SchedulerException;

使调用了
shutdown() 方法之后,你不怕未克还调用 Scheduler 实例的 start() 方法了。
这是以 shutdown() 方法销毁了吗 Scheduler
创建的备的资源(线程,数据库连接等)。 你或许用Standby 模式:设置
Scheduler 为 standby 模式会促成 Scheduler搜寻要实行之 Job
的线程被中止下来
停止 Scheduler

Java代码
 )

  1. //waitForJobsToComplete 是否让眼前在开展的Job正常履好才已Scheduler  
  2. public void shutdown(boolean waitForJobsToComplete) throws SchedulerException;  
  3. public void shutdown() throws SchedulerException;  

    //waitForJobsToComplete 是否被眼前正在进展的Job正常实施好才打住Scheduler
    public void shutdown(boolean waitForJobsToComplete) throws SchedulerException;
    public void shutdown() throws SchedulerException;

旁管理Scheduler 方法见API… 管理 Job 什么是
Quartz Job? 一个Quart
Job就是一个任何一个蝉联job或job子接口的Java类,你可以据此之类似做另外业务!
org.quartz.Job 接口

Java代码
 )

  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代码
 )

  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操作。

图也表达没有履就的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代码
 )

  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代码
 )

  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代码
 )

  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代码
 )

  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代码
 )

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

    }


图7 job listener参与job的实践生命周期
报全局监听器

Java代码
 )

  1. scheduler.addGlobalJobListener(jobListener);  

    scheduler.addGlobalJobListener(jobListener);

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

Java代码
 )

  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代码
 )

  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代码
 )

  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代码
 )

  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代码
 )

  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代码
 )

  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代码
 )

  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 接口.
图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 类。

希冀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代码
 )

  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代码
 )

  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集群
图6 一个 Quartz
集群被的每个节点是一个单独的 Quartz 应用,它又治本在其它的节点。
需要各自对每个节点分别启动或终止。不像应用服务器的集群,独立的 Quartz
节点并无与外一个节点可能管理节点通信。 Quartz
应用是透过数据库表来感知到任何一样使用。
布局集群

Xml代码
 )

  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代码
 )

  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代码
 )

  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代码
 )

  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代码
 )

  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代码
 )

  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容器对象
    @Service(“springBeanService”)
    public class SpringBeanService 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代码
 )

  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代码
 )

  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代码
 )

  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




延续要开发力量跟问题
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,变成单独的劳务,供其他系统调度使用使用

相关文章