也谈Quartz

也谈Quartz

 

       Quartz是什么?google一下,你就知道^_^

    java开发领域,quartz基本就是开源的作业调度(或称定时任务)代名词。她可以与J2EEJ2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个, 甚至是好几万个Jobs这样复杂的日程序表。Jobs可以做成标准的Java组件或 EJBs

    前一段时间,因项目开发需要,采用她,事实证明这种选择是完全正确的。

    刚开始用时,也是手忙脚乱,也不知从哪里下手,好在现在有互联网,下载《Quartz开发指南》,pdf格式的。

    思路初步定下来:为避免采用quartz- 1.5.2 /docs/dbTables下数据表脚本创建大量数据表,自建triggerjob数据表。但后来证明这个思路本身就有问题,暂且不表。

    按《指南》精神,若想持久化作业调度信息,就必须配置其自带quartz.properties,该文件默认配置把相关信息保存本地xml文件中,改为以下:

       org.quartz.scheduler.instanceName = DefaultQuartzScheduler

       #org.quartz.scheduler.instanceName = QuartzScheduler

       #org.quartz.scheduler.instanceId = AUTO

       org.quartz.scheduler.rmi.export = f al se

       org.quartz.scheduler.rmi.proxy = f al se

       org.quartz.scheduler.wrapJobExecutionInUserTransaction = f al se

      

       org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

       org.quartz.threadPool.threadCount = 1

       org.quartz.threadPool.threadPriority = 5

       org.quartz.threadPool.threadsInheritContextClassLoaderOfIniti al izingThread = true

      

       org.quartz.jobStore.misfireThreshold = 60000

      

       org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

       org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

       org.quartz.jobStore.useProperties = f al se

       org.quartz.jobStore.dataSource = myDS

       org.quartz.jobStore.tablePrefix = QRTZ_

       org.quartz.jobStore.isClustered = f al se

      

       org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver

       org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@127.0.0.1:1521:yp

       org.quartz.dataSource.myDS.user = yp

       org.quartz.dataSource.myDS.password = yp

       org.quartz.dataSource.myDS.maxConnections = 5

      

       org.quartz.plugin.triggHistory.class =org.quartz.plugins.history.LoggingTriggerHistoryPlugin

       org.quartz.plugin.triggHistory.triggerFiredMessage = Trigger{1}.{0}firedjob{6}.{5}at:{4,date,HH:mm:ssMM/dd/yyyy}

       org.quartz.plugin.triggHistory.triggerCompleteMessage = Trigger{1}.{0}completedfiringjob{6}.{5}at{4,date,HH:mm:ssMM/dd/yyyy}withresultingtriggerinstructioncode:{9}

       然后,按以下步骤运作:  Quartz示意图

从上图可以看到有一个jobDetail,其实她就是你要指定的job信息及内容。该内容说白了就是让quartz具体做什么工作,假若一定要用一个词来概括jobDetail,那就是:whatquartz之所以被人到处称道,大概是因为她有一个很好的理念:分离工作本身定义和内容。

从上图可以看到triggerSimpleTriggerCronTrigger两种。前者简单定时器,只在某个时刻执行,或者,在某个时刻开始,然后按照某个时间间隔重复执行,值得一提的是结束时间属性优先级高于重复次数属性,我一般用她做测试,然后判断我定义的job有没有问题;后者就是功能强大cron表达式定时器。假若一定要用一个词来概括trigger,那就是:when

从上图可以看到分别定义jobDetailtrigger,当然还要有一个容器把她们全包括进来,就是scheduler,有了她就可以安排任务了,她可以做很多高难度动作哟,比如startstop等。假若一定要用一个词来概括scheduler,那就是:how

按原来思路,当走到创建jobDetail时,就开始碰壁了,后来想想,碰壁也是正常的,干革命哪有一帆风顺的?!因为创建jobDetail时,你调用quartzapi,该api会自动把你自定义job信息存入其相应系统表内。这些系统表数据由其自身维护,不用你操心^_^

   切,我还懒得操心,哈

但笔者使用过程中发现一个问题,采用simpletrigger,若作业调度全部完成后,该类trigger会自动删除。难道我辛辛苦苦创建的trigger,若执行完成再想执行同样调度,还要重新创建?此时我自己构建trigger数据表就派上用场了,我只要控制与quartz系统表基本一致就可以了。

刚开始,我还后悔,没参悟透彻quartz全部理念,自建一套quartz表结构,还要维护自建表与系统表中trigger状态一致性,好麻烦。

现在quartz系统表调度完成后自动删除,我自建那个trigger表中该信息并不删除,只要改变其状态就可以了。

若系统表发现已经有待创建的自建表jobDetail或trigger信息,会报错有冲突,怎么办?也好办呀,创建前先判断一下,若存在就不再创建;反之,就创建。

塞翁失马,焉知祸福。

 

 

闲话少说,以代码为证吧:

TestQuartz.java

 

package test;

 

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.SQLException;

import java.text.DateFormat;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.Loc al e;

import org.quartz.JobDetail;

import org.quartz.Scheduler;

import org.quartz.SchedulerFactory;

import org.quartz.SimpleTrigger;

import org.quartz.impl.StdSchedulerFactory;

 

/**

 * 调度器测试

 * @version 2.0 Apr 8, 2008

 * @author 俞鹏

 * @since JDK1.5

 */

public class TestQuartz {

    private SchedulerFactory scheduleFactory = null;

 

    public static void main(String[] args){

       TestQuartz test = new TestQuartz();

       try     {

          test.startSchedule();

       }

       catch (Exception e)      {

         e.printStackTrace();

       }

     }//end main

 

     /**

      * 调度

      */   

     public void startSchedule() throws Exception

     { 

         //创建jotDetail,其中TestJob自定义job

         JobDetail jobDetail =

         new JobDetail("testJob01", Scheduler.DEFAULT_GROUP, TestJob.class);

          //结束时间    

         long end = System.currentTimeMillis() + 9000L ;

        //创建简单触发器,执行2次,每1秒执行一次,到9秒后结束

         SimpleTrigger trigger = new SimpleTrigger("testTrigger01",null,strToDate(" 2008 -06-17 10:27:00"),null,2,1000);

       //CronTrigger trigger = new CronTrigger("trigger6", Scheduler.DEFAULT_GROUP, "0 0 0 ? * *");

       //创建调度器,若其他没特别用途,用以下默认方式就可以

        SchedulerFactory sf = new StdSchedulerFactory();

        Scheduler scheduler = sf.getScheduler();

//加载jobDetailtrigger

         scheduler.scheduleJob(jobDetail, trigger);

//调度器启动

         scheduler.start();

    }

}

 

TestJob.java(自定义job)

package test;

 

import java.sql.Blob;

import java.sql.Clob;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.ResultSetMet aDa ta;

import java.sql.SQLException;

import java.sql.Statement;

 

import java.util.ArrayList;

import java.util.Hashtable;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

 

 

import org.quartz.JobDataMap;

import org.quartz.JobDetail;

import org.quartz.JobExecutionContext;

import org.quartz.SchedulerException;

import org.quartz.StatefulJob;

/**

 * 自定义job

 * @version 2.0 Apr 8, 2008

 * @author 俞鹏

 * @since JDK1.5

*/

public class TestJob implements  Job{

//  覆盖execute方法

    public void execute(JobExecutionContext context) {

 

    //  每个Job都有独立的JobDetail

       JobDetail jobDetail = context.getJobDetail();

    //  nameJob定义中指定

       String jobName = jobDetail.getName();

    //  每个Job都有一个Job Data map来存放扩展的信息

       JobDataMap dataMap = jobDetail.getJobDataMap();

       try {

    //打印trigger状态,这个也可算本job另一功能吧   

    System.out.println("trigState:"+context.getScheduler().getTriggerState("testTrigger01", null));

       } catch (SchedulerException e) {

           e.printStackTrace();

       }

       //job实现功能

       System.out.println("Hello World!!!");

    }

 

08-16
Quartz 是一个功能强大的任务调度框架,广泛应用于企业级应用中,用于处理复杂的任务调度需求。以下是关于 Quartz 的使用指南、配置方法以及问题解决的详细说明。 ### 使用指南 Quartz 提供了多种方式来定义和管理任务。核心组件包括 `Job`(任务)、`Trigger`(触发器)和 `Scheduler`(调度器)。任务的执行逻辑通常通过实现 `Job` 接口来定义,而触发器则决定了任务何时执行。调度器负责管理任务和触发器的生命周期。 以下是一个简单的任务实现示例: ```java import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class SimpleJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("任务正在执行..."); } } ``` 接下来,需要定义一个触发器来指定任务的执行时间。可以使用 `CronTrigger` 来基于 CRON 表达式进行调度: ```java import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; public class QuartzExample { public static void main(String[] args) throws Exception { // 创建任务详情 JobDetail job = JobBuilder.newJob(SimpleJob.class) .withIdentity("simpleJob", "group1") .build(); // 创建触发器,每分钟执行一次 CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity("cronTrigger", "group1") .withSchedule(CronScheduleBuilder.cronSchedule("0 * * * * ?")) .build(); // 获取调度器并启动 SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.start(); // 将任务和触发器注册到调度器 scheduler.scheduleJob(job, trigger); } } ``` ### 配置方法 Quartz 提供了丰富的配置选项,以支持持久化、集群部署和高可用性等高级特性。这些配置通常在 `quartz.properties` 文件中进行设置。 #### 持久化配置 为了确保任务调度的状态在系统重启后仍然保留,可以将 Quartz 配置为使用数据库进行持久化。以下是一个简单的数据库配置示例: ```properties org.quartz.scheduler.instanceName=MyScheduler org.quartz.threadPool.threadCount=3 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.dataSource=myDS org.quartz.dataSource.myDS.driver=com.mysql.cj.jdbc.Driver org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost:3306/quartz org.quartz.dataSource.myDS.user=root org.quartz.dataSource.myDS.password=root org.quartz.dataSource.myDS.maxConnections=5 ``` #### 集群配置 在集群环境中,多个 Quartz 实例可以共享同一个数据库,以实现任务的高可用性和负载均衡。以下是一个集群配置的示例: ```properties org.quartz.scheduler.instanceName=MyClusteredScheduler org.quartz.scheduler.instanceId=AUTO org.quartz.threadPool.threadCount=3 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.dataSource=myDS org.quartz.jobStore.isClustered=true org.quartz.jobStore.clusterCheckinInterval=20000 org.quartz.dataSource.myDS.driver=com.mysql.cj.jdbc.Driver org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost:3306/quartz org.quartz.dataSource.myDS.user=root org.quartz.dataSource.myDS.password=root org.quartz.dataSource.myDS.maxConnections=5 ``` ### 问题解决 在使用 Quartz 时,可能会遇到一些常见问题。以下是一些解决方案和最佳实践: 1. **任务未按预期执行**:检查 CRON 表达式是否正确,并确保调度器已启动。可以通过日志查看调度器的状态和任务的执行情况。 2. **持久化失败**:确保数据库连接配置正确,并且数据库表结构符合 Quartz 的要求。可以使用 Quartz 提供的 SQL 脚本来创建所需的表。 3. **集群节点无法同步**:检查集群节点之间的网络连接,并确保所有节点使用相同的数据库配置。此外,确认 `clusterCheckinInterval` 设置合理,以便节点能够及时更新状态。 4. **任务执行异常**:可以通过实现 `SchedulerListener` 接口来监听任务和触发器的状态变化,并在发生错误时进行处理。以下是一个简单的 `SchedulerListener` 实现示例: ```java import org.quartz.SchedulerListener; import org.quartz.SchedulerException; public class MySchedulerListener implements SchedulerListener { @Override public void jobScheduled(Trigger trigger) { System.out.println("任务已调度: " + trigger.getKey()); } @Override public void jobUnscheduled(String triggerName, String triggerGroup) { System.out.println("任务已取消: " + triggerName + "/" + triggerGroup); } @Override public void triggerFinalized(Trigger trigger) { System.out.println("触发器已最终化: " + trigger.getKey()); } @Override public void triggersPaused(String triggerName, String triggerGroup) { System.out.println("触发器已暂停: " + triggerName + "/" + triggerGroup); } @Override public void triggersResumed(String triggerName, String triggerGroup) { System.out.println("触发器已恢复: " + triggerName + "/" + triggerGroup); } @Override public void jobsPaused(String jobName, String jobGroup) { System.out.println("任务已暂停: " + jobName + "/" + jobGroup); } @Override public void jobsResumed(String jobName, String jobGroup) { System.out.println("任务已恢复: " + jobName + "/" + jobGroup); } @Override public void schedulerError(String msg, SchedulerException cause) { System.out.println("调度器错误: " + msg); cause.printStackTrace(); } @Override public void schedulerStarted() { System.out.println("调度器已启动"); } @Override public void schedulerInStandbyMode() { System.out.println("调度器进入待机模式"); } @Override public void schedulerShutdown() { System.out.println("调度器已关闭"); } @Override public void schedulingDataCleared() { System.out.println("调度数据已清除"); } } ``` 通过以上指南和配置方法,可以有效地使用 Quartz 进行任务调度,并解决可能出现的问题。结合实际业务需求,合理配置持久化和集群选项,并应用安全机制,可以确保任务调度系统的安全、稳定和可靠 [^2]。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值