前言
之前写了一个quart指南,事后自己看都觉得十分的凌乱,不知从何下手,于是决定再写一篇,争取比上一篇写的好。
quartz优势
quartz实现了监听时钟,定时执行
quartz实现了持久化任务到数据库,并从数据库中读取数据定时执行
优化执行效率,多线程管理,配置线程池
等等
从一个实际的例子入手
需求
提供接口,实现
1. 调用可以立即执行一个service(其实这个不是quartz的优势)
2. 调用可以周期性执行一个service ,如果项目停掉,再重启可重新启动对这个servcie的调用
【注: 由于我用在ofbiz上,这个service是指ofbiz中的service,其实就是指一个具体实现某个业务的java方法,可替换修改】
分析
- 项目启动时应该吧执行job,监测trigger,的scheduler 启动起来(job和trigger分别是任务和触发器,不在详述,上一篇有说明)
- 启动后应该有两个方法供我们调用,分别实现上面的立即执行和周期性执行
- 配置quartz的数据库,以持久化job和trigger的数据,方便下次调用
实现
准备工作,官网下载quartz包,将里面的jar包导入到项目
1.在web.xml中配置了启动servlet,来启动scheduler
<servlet>
<servlet-name>SchedulerServlet</servlet-name>
<servlet-class>cn.metasolo.manager.StartScheduler</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
StartScheduler启动和停止
//继承HttpServlet 重写init()和destroy()方法
public class StartScheduler extends HttpServlet {
/**
* start quartz scheduler
*/
private static Logger logger =LogManager.getLogger("quartz");
private static final long serialVersionUID = 1L;
public void init(){
SchedulerManager.start();
logger.info("[quart scheduler start]");
}
public void destroy(){
SchedulerManager.shutdown();
logger.info("[quart scheduler shutdown]");
}
}
2.创建SchedulerManager来提供api
代码看起来有点长,只是包含了4个方法1个内部类,方法用途分别是:启动,停止,立即执行某个service,周期性执行某个service,内部类是用于实现Job的。
注:这里的delegator 和dispatcher 是ofbiz 中的一些工具类,你在用的时候完全可以去掉,把serviceName 想办法换成你的java方法调用。
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import static org.quartz.CronScheduleBuilder.cronSchedule;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ofbiz.entity.Delegator;
import org.ofbiz.entity.DelegatorFactory;
import org.ofbiz.service.GenericServiceException;
import org.ofbiz.service.LocalDispatcher;
import org.ofbiz.service.ServiceContainer;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
public class SchedulerManager {
private static Logger logger =LogManager.getLogger("quartz");
private static Scheduler sched;
private static JobDetail job;
private static CronTrigger trigger;
private static Delegator delegate;
private static LocalDispatcher dispatcher;
public static void start() {
try {
SchedulerFactory sf = new StdSchedulerFactory();
try {
sched=sf.getScheduler();
delegate = DelegatorFactory.getDelegator("default");
dispatcher = ServiceContainer.getLocalDispatcher(delegate.getDelegatorName(), delegate);
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(sched!=null&&!sched.isStarted()){
sched.start();
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
public static void shutdown() {
try {
if(sched!=null&&!sched.isShutdown()){
sched.shutdown();;
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* addJobRunCyclicity
* @author xkl
* @param serviceName
* @param maps
* @param cyclicity
* <b>execute cyclicity</b>
*/
public static void addJobRunCyclicity(String serviceName,Map<String, Object> maps,String cyclicity){
//如果已经存在任务列表里,不再添加
JobKey key = new JobKey(serviceName, "serviceGroup");
try {
if(sched.checkExists(key)){
logger.info("This job is exists!add failed!");
return;
}
} catch (SchedulerException e1) {
logger.error("Check job key exists error:"+e1.getMessage());
}
job = newJob(ServiceJob.class).withIdentity(serviceName, "serviceGroup").build();
job.getJobDataMap().put("conditions", maps);
trigger = newTrigger().withIdentity("trigger_"+serviceName,"serviceGroup").startNow().withSchedule(cronSchedule(cyclicity)).build();
try {
sched.scheduleJob(job, trigger);
logger.info("[execute service "+serviceName+" by cyclicity of "+ cyclicity+"]");
} catch (SchedulerException e) {
logger.error("Add scheduler Job error:"+e.getMessage());
}
}
/**
* addJobRunImmediately
* @param serviceName
* @param maps
*<p> execute immediately</p>
*/
public static void addJobRunImmediately(String serviceName,Map<String, Object> maps){
//如果已经存在任务列表里,不再添加
JobKey key = new JobKey(serviceName, "serviceGroup");
try {
if(sched.checkExists(key)){
logger.info("This job is exists!add failed!");
return;
}
} catch (SchedulerException e1) {
logger.error("Check job key exists error:"+e1.getMessage());
}
job = newJob(ServiceJob.class).withIdentity(serviceName, "serviceGroup").build();
job.getJobDataMap().put("conditions", maps);
SimpleTrigger trigger = (SimpleTrigger) newTrigger().withIdentity("trigger_"+serviceName, "group1").startNow().build();
try {
sched.scheduleJob(job, trigger);
logger.info("[execute service "+serviceName+" immediately]");
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static class ServiceJob implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
JobKey jobKey = context.getJobDetail().getKey();
Map<String,Object> maps = context.getJobDetail().getJobDataMap();
Map<String,Object> conditions = null;
if(maps.get("conditions")!=null){
conditions = (Map<String,Object>)maps.get("conditions");
}
dispatcher.runSync(jobKey.getName(),conditions);
logger.info("执行了ServiceJob任务");
} catch (GenericServiceException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
3.如何持久化:持久化需要配置数据库了,quart包中的 \quartz-2.2.3\docs\dbTables 目录中有初始化创建各种数据表的 sql 语句,根据你的需要创建不同的数据库以及 数据库表
我用的是tables_postgres.sql 不贴了,自己打开找一下吧
生成表后还需要配置一下quartz.properties配置文件:(名字不能改,放在classpath下)
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName: SchedulerManager
org.quartz.scheduler.instanceId: AUTO
org.quartz.scheduler.skipUpdateCheck: true
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 5
org.quartz.threadPool.threadPriority: 5
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold: 60000
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.useProperties: false
org.quartz.jobStore.dataSource: myDS
org.quartz.jobStore.tablePrefix: QRTZ_
org.quartz.jobStore.isClustered: false
#============================================================================
# Configure Datasources
#============================================================================
org.quartz.dataSource.myDS.driver: org.postgresql.Driver
org.quartz.dataSource.myDS.URL: jdbc:postgresql://localhost/ErpQuartz
org.quartz.dataSource.myDS.user:****
org.quartz.dataSource.myDS.password:***
org.quartz.dataSource.myDS.maxConnections: 5