//本着以交流经验和学习的态度来分享知识,如果有误,请批评指出,不胜感激!
现在企业中基本均有涉及到任务调度和异步执行器,在Java方向,提供了开源的Quartz、JDK提供了Timer。在以上基础前提下,Java5.0通过java.util.concurrent这个新包以及它下边的诸多类和接口,提供了方便的线程池调用。
在本篇文章中,我们主要使用Quartz来作为解决任务调度的工具。为什么不是用JDK提供的方法?
//(因为Timer解决基础的调度室没有问题的,但是如果处理复杂逻辑调度和类似于每个星期一12:00处理任务,这种复杂时间,就有点捉急了)。
废话不多说,我们先做一个‘简单’的Quartz Demo来'简单'介绍一下Quartz的基本使用方法和功能
我先简单介绍一下Quartz的核心接口和类(如果已经了解的请略过):
1.Job,这是一个接口,并且只有一个void execute(JobExecutionContext context) throws JobExecutionException 的方法。这个方法定义了需要调度的方法,开发者在使用Quartz并定义调度任务时候,需要实现这个接口并且重写此方法。
2.JobDetail,看名字也知道这是Job的实现类,当Quartz执行Job时,它会接受JobDetail这个实现类,通过newInstance()的反射调用机制来实例化一个Job,也就是说,在实例化Job时,需要有一个返回值来接受实例化的Job和一些静态信息。
3.Trigger,这是一个类,包括两个子类,主要是触发Job执行的时间触发规则,主要有SimpleTrigger和CronTrigger两个子类;当仅需要触发一次或者以固定检核周期性执行时,SimpleTrigger一定是最合适的选择,当如果要执行复杂的调度规则时,则可以使用CronTrigger。
4.Scheduler,代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,二者在Scheduler中有各自的组件、名称和组
暂时需要了解到这4样核心API,说一说我的理解就是:当你创建一个Job和Trigger之后,将这两个放到Quartz容器 Scheduler中,通过启动Scheduler来启动Job
现在,我们创建一个工程来实际使用一下
这个是完成后的Demo所有文件
我们一步一步来,在Maven中首先引入依赖,如下图
org.quartz-scheduler
quartz
2.2.1
org.quartz-scheduler
quartz-jobs
2.2.1
org.slf4j
slf4j-log4j12
1.6.6
然后创建一个针对RAM存储的JOB类,这个类是Job 的实现类,在实现Job接口后需要重写execute方法,在方法中定义需要执行的任务,举个例子如下
package quartz.demo;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RAMJob implements Job{
private Logger log = LoggerFactory.getLogger(RAMJob.class);
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
log.info("我也不知道这个啥时候执行,反正这个是Job 的实现类");
}
}
然后我们在创建一个类,用来执行一个简单的任务执行器,在新的类中我们使用main方法来启动执行,在执行之前,我们需要先定义一个JobDetail对象,如图:
//创建一个JobDetail
JobDetail jobDetail = JobBuilder.newJob(RAMJob.class)
.withDescription("调用JobDemo")
.withIdentity("Job's name", "Job's Group")
.build();
log.info("描述任务:{}" + jobDetail.getDescription());
具体的任务描述和设置都已在方法中定义,不过想了解的盆友可以点开看看源码,都很简单,这里不再阐述。
在定义完Job后,我们需要再定义一个Trigger触发规则,如下:
//创建一个trigger触发规则
Trigger trigger = TriggerBuilder.newTrigger()
.withDescription("创建一个Trigger触发规则")
.startAt(new Date())
.withIdentity("Trigger's Name", "Trigger's Group")
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(10,5))
.build();
这里可能很多没看源码的人看不懂或者是别的情况,我简单解释一下各个方法的作用,我们通过TriggerBuilder新建一个TriggerBuilder对象,
.withDescription(String description);根据字面意思,这个方法表示针对这个Trigger进行描述;
.startAt(Date triggerStartTime):根据字面单词意思,这就是设置Trigger规则启动时间的方法;
.withIdentity();这个方法是设置Trigger基本信息的方法,通过源码我们可以看到有三个重载的方法,分别是:.withIdentity(String name);.withIdentity(String name , String group);.withIdentity(TriggerKey triggerKey),这三个方法的本身操作其实是无差别的,都是要给初始化给TriggerKey对象赋值;
在完成上面两个基本任务后,我们开始构建Quartz容器,要注意一点,Quartz容器(Scheduler)是独立运行的,Scheduler可以将Trigger绑定到某一个JobDetail上,当Trigger被触发时,对应的Job就被执行,一个Job可以对应多个Trigger,一个Trigger只能对应一个Job,我们一般通过SchedulerFactory创建一个Scheduler实例;如下图:
//创建一个调度器,也就是一个Quartz容器
//声明一个scheduler的工厂schedulerFactory
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
//通过schedulerFactory来实例化一个Scheduler
Scheduler scheduler = schedulerFactory.getScheduler();
//将Job和Trigger注册到scheduler容器中
scheduler.scheduleJob(jobDetail,trigger);
最后,我们启动scheduler容器:
//启动容器
log.info("JOB开始启动);
scheduler.start();
此时我们就完全启动了一个任务异步执行,启动日志如下:
10:22:35,093 [main] INFO quartz.demo.SimpleTriggerTest - Job开始执行
10:22:35,099 [main] INFO quartz.demo.SimpleTriggerTest - 描述任务:{}调用JobDemo
10:22:35,167 [main] INFO com.mchange.v2.log.MLog - MLog clients using log4j logging.
10:22:35,458 [main] INFO com.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9.1.1 [built 15-March-2007 01:32:31; debug? true; trace: 10]
10:22:35,482 [main] INFO org.quartz.impl.StdSchedulerFactory - Using default implementation for ThreadExecutor
10:22:35,494 [main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
10:22:35,495 [main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.2.1 created.
10:22:35,496 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Using thread monitor-based data access locking (synchronization).
10:22:35,496 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - JobStoreTX initialized.
10:22:35,497 [main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.2.1) 'dufy_test' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.
10:22:35,497 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'dufy_test' initialized from default resource file in Quartz package: 'quartz.properties'
10:22:35,497 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.2.1
10:22:35,531 [main] INFO com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1bqrhg09w1in8fcy1gr7r0t|39a054a5, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1bqrhg09w1in8fcy1gr7r0t|39a054a5, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://140.143.205.128:3306/c_test?characterEncoding=utf8&useSSL=true, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 10, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
10:22:36,368 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Freed 0 triggers from 'acquired' / 'blocked' state.
10:22:36,412 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down.
10:22:36,412 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovery complete.
10:22:36,434 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 'complete' triggers.
10:22:36,455 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 stale fired job entries.
10:22:36,497 [main] INFO org.quartz.core.QuartzScheduler - Scheduler dufy_test_$_NON_CLUSTERED started.
10:22:36,497 [main] INFO quartz.demo.SimpleTriggerTest - Job结束
10:22:36,891 [dufy_test_Worker-1] INFO quartz.demo.RAMJob - 我也不知道这个啥时候执行,反正这个是Job 的实现类
10:22:40,273 [dufy_test_Worker-2] INFO quartz.demo.RAMJob - 我也不知道这个啥时候执行,反正这个是Job 的实现类
10:22:45,273 [dufy_test_Worker-3] INFO quartz.demo.RAMJob - 我也不知道这个啥时候执行,反正这个是Job 的实现类
10:22:50,269 [dufy_test_Worker-4] INFO quartz.demo.RAMJob - 我也不知道这个啥时候执行,反正这个是Job 的实现类
10:22:55,274 [dufy_test_Worker-5] INFO quartz.demo.RAMJob - 我也不知道这个啥时候执行,反正这个是Job 的实现类
10:23:00,275 [dufy_test_Worker-6] INFO quartz.demo.RAMJob - 我也不知道这个啥时候执行,反正这个是Job 的实现类
10:23:05,269 [dufy_test_Worker-7] INFO quartz.demo.RAMJob - 我也不知道这个啥时候执行,反正这个是Job 的实现类
10:23:10,272 [dufy_test_Worker-8] INFO quartz.demo.RAMJob - 我也不知道这个啥时候执行,反正这个是Job 的实现类
10:23:15,404 [dufy_test_Worker-9] INFO quartz.demo.RAMJob - 我也不知道这个啥时候执行,反正这个是Job 的实现类
10:23:20,365 [dufy_test_Worker-10] INFO quartz.demo.RAMJob - 我也不知道这个啥时候执行,反正这个是Job 的实现类
从日志中我们可以看出,任务是以每5s的频率执行的,执行了10个周期,与上边的定义.withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(10,5))的一致,任务结束
到此,一个基本的Quartz任务调度demo就结束了,下一期我们继续讲解Quartz与Spring的整合
谢谢!!!