1. 如何实现Quartz远程控制;
(1) 通过数据库实现远程quartz的控制:
服务端(任务的执行端)例如 quartz_jobs.jar 通过 java -jar quartz_jobs.jar 执行任务;
(一) 创建quartz 数据库 并创建quartz的表(sql由官方提供) 下载 quartz-2.2.2-distribution.tar 包 解压后 \quartz-2.2.2\docs\dbTables 文件下(这里以MySQL为例)
创建 quartz任务存储的数据库这里叫 crawl_quartz
并将 tables_mysql_innodb.sql 导入数据库中
(二) 在项目的根目录下 src 下添加 quartz.properties 文件 有关配置参看官方文档:https://www.quartz-scheduler.org/generated/2.2.2/html/qtz-all/#page/quartz-scheduler-webhelp%2F_qs_all.1.041.html%23
#==============================================================
#Configure Main Scheduler Properties
#==============================================================
org.quartz.scheduler.instanceName = v1_crawl_scheduler
org.quartz.scheduler.instanceId = AUTO
#==============================================================
#Configure JobStore (配置quartz 任务的存储方式 : 包括 数据库存储 和 内存存储)
#==============================================================
# 以下为数据库的存储并需要配置数据库
org.quartz.scheduler.instanceName = v1_crawl_scheduler
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.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = v1_dataSource
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
# 本地内存存储(不需要配置数据库)
#org.quartz.jobStore.misfireThreshold = 60000
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
#==============================================================
#Configure DataSource (指定quartz 数据库 并配置 就是第一步说的那个数据库)
#==============================================================
org.quartz.dataSource.v1_dataSource.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.v1_dataSource.URL = jdbc:mysql://192.168.5.8:3306/crawl_quartz?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
org.quartz.dataSource.v1_dataSource.user = test
org.quartz.dataSource.v1_dataSource.password = test
org.quartz.dataSource.v1_dataSource.maxConnections = 20
#==============================================================
#Configure ThreadPool (quartz 的线程控制)
#==============================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
控制端实现 (调用里面的方法,会自动修改配置数据中的数据)
package org.crawl.quartz;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.log4j.Logger;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.Trigger.TriggerState;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import cn.v1.video.crawl.task.CarwlJob;
/**
* 定时任务管理器
* @author hp
*
*/
public class SchedulerFactoryManager {
private static final Logger logger=Logger.getLogger(SchedulerFactoryManager.class);
private final static SchedulerFactory schedulerFactory = new StdSchedulerFactory();
//单例(构造方法私有化)
private SchedulerFactoryManager(){};
//获取Scheduler 对象
private static Scheduler getScheduler() throws SchedulerException{
return schedulerFactory.getScheduler();
}
//关闭定时器
public static void shutdown(){
try {
Scheduler scheduler=getScheduler();
if(scheduler.isStarted()){
scheduler.shutdown();
logger.info("---关闭当前的定时器---");
}
} catch (SchedulerException e) {
logger.error("关闭定时器异常", e);
}
}
//启动定时器
public static void start(){
try {
Scheduler scheduler= getScheduler();
if(scheduler.isShutdown()){
scheduler.start();
logger.info("---启动定时器---");
}
} catch (SchedulerException e) {
logger.error("启动定时器异常", e);
}
}
/**
* 暂停任务
*/
public static boolean pauseJob(Integer taskId,Integer groupId){
boolean flag=false;
try {
Scheduler scheduler=getScheduler();
JobKey jobKey= JobKey.jobKey(String.valueOf("task_"+taskId),"group_"+groupId);
scheduler.pauseJob(jobKey); //暂停任务
logger.info("----暂停任务---"+"task_"+taskId+",group_"+taskId+"--------");
flag=true;
} catch (SchedulerException e) {
logger.error("---暂停任务异常---", e);
}
return flag;
}
/**
* 恢复任务
* @param taskId
* @param groupId
* @return
*/
public static boolean resumeJob(Integer taskId,Integer groupId){
boolean flag=false;
try {
Scheduler scheduler=getScheduler();
JobKey jobKey= JobKey.jobKey(String.valueOf("task_"+taskId),"group_"+groupId);
scheduler.resumeJob(jobKey);
flag=true;
} catch (SchedulerException e) {
logger.error("---恢复任务异常---", e);
}
return flag;
}
/**
* 删除任务
* @param taskId
* @param groupId
* @return
*/
public static boolean delJob(Integer taskId,Integer groupId){
boolean flag=false;
try {
Scheduler scheduler=getScheduler();
JobKey jobKey= JobKey.jobKey(String.valueOf("task_"+taskId),"group_"+groupId);
scheduler.deleteJob(jobKey);
flag=true;
} catch (SchedulerException e) {
logger.error("---恢复任务异常---", e);
}
return flag;
}
//添加测试任务并立即执行(只执行一次)
public static boolean addTestJob(Integer taskId,Integer groupId,String taskName,final Map<String, Object> jobMap){
TestScheduler testScheduler=new TestScheduler();
return testScheduler.execTestJob(taskId, groupId, taskName, jobMap);
}
/**
* 删除所有的任务
* @return
*/
public static boolean delAllJobs(){
boolean flag=false;
try {
Scheduler scheduler=getScheduler();
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys=scheduler.getJobKeys(matcher);
scheduler.deleteJobs(new ArrayList<JobKey>(jobKeys));
flag=true;
} catch (SchedulerException e) {
logger.error("---恢复任务异常---", e);
}
return flag;
}
/**
* 修改任务( 先删除后 添加)
* @param taskId
* @param groupId
* @param cron
* @param jobDataMap
*/
public static boolean modifyJob(Integer taskId,Integer groupId,String cron,String taskName,JobDataMap jobDataMap){
boolean flag=false;
try {
Scheduler scheduler=getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(String.valueOf("task_"+taskId),"group_"+groupId);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if(trigger!=null){ //如果此触发器存在
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
scheduler.rescheduleJob(triggerKey, trigger);
logger.info("触发器 [ task_"+taskId+" group_"+groupId+" 已经存在 更新它 ]");
}else{
trigger = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.forJob(String.valueOf("task_"+taskId),"group_"+groupId)
.withDescription(taskName)
.build();
}
JobKey jobKey= JobKey.jobKey(String.valueOf("task_"+taskId),"group_"+taskId);
JobDetail jobDetail=scheduler.getJobDetail(jobKey);
if(jobDetail!=null){ //此任务存在删除任务
delJob(taskId, groupId);
}
scheduler.scheduleJob(jobDetail, trigger);
flag=true;
} catch (SchedulerException e) {
logger.error("---修改任务异常---", e);
}
return flag;
}
//加载测试的内部类
private static class TestScheduler{
private static final StdSchedulerFactory sf = new StdSchedulerFactory();
{
try {
Properties props = new Properties();
props.put("org.quartz.scheduler.instanceName", "testScheduler");
props.put("org.quartz.threadPool.threadCount", "10");
sf.initialize(props);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
public Scheduler getTestScheduler(){
try {
Scheduler scheduler = sf.getScheduler();
return scheduler;
} catch (SchedulerException e) {
e.printStackTrace();
}
return null;
}
/**
* 添加用户抓取测试 执行一次
* @param jobDetail
* @return
*/
public boolean execTestJob(Integer taskId,Integer groupId,String taskName,Map<String, Object> jobMap){
boolean flag=false;
try {
Scheduler scheduler=this.getTestScheduler();
if(scheduler==null){
return false;
}
final JobDataMap dataMap=new JobDataMap();
dataMap.putAll(jobMap);
JobDetail jobDetail=JobBuilder.newJob()
.ofType(CarwlJob.class)
.setJobData(dataMap)
.withIdentity(String.valueOf("task_test_"+taskId),"group_test_"+groupId)
.build();
TriggerKey triggerKey = TriggerKey.triggerKey(String.valueOf("task_test_"+taskId),"group_test_"+groupId);
Trigger trigger = scheduler.getTrigger(triggerKey);
if(trigger !=null){
trigger.getTriggerBuilder().withIdentity(triggerKey).startNow()
.withDescription(taskName)
.forJob(jobDetail)
.build();
scheduler.rescheduleJob(triggerKey, trigger);
}else{
trigger=TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.startNow()
.withDescription(taskName)
.forJob(jobDetail)
.build();
}
JobKey jobKey= JobKey.jobKey(String.valueOf("task_test_"+taskId),"task_test_"+taskId);
if(scheduler.getJobDetail(jobKey)!=null){ //如果该任务存在就替换
scheduler.addJob(jobDetail, true);
}
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
flag=true;
} catch (SchedulerException e) {
logger.error("---添加任务异常---", e);
}
return flag;
}
}
/**
* 添加任务
* @param jobDetail
* @return
*/
public static boolean addJob(Integer taskId,Integer groupId,String cron,String taskName,Map<String, Object> jobMap){
boolean flag=false;
try {
Scheduler scheduler=getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(String.valueOf("task_"+taskId),"group_"+groupId);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if(trigger!=null){ //如果此触发器存在
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
scheduler.rescheduleJob(triggerKey, trigger);
logger.info("触发器 [ task_"+taskId+" group_"+groupId+" 已经存在 更新它 ]");
}else{
trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.forJob(String.valueOf("task_"+taskId),"group_"+groupId)
.withDescription(taskName)
.build();
}
final JobDataMap dataMap=new JobDataMap();
dataMap.putAll(jobMap);
JobDetail jobDetail=JobBuilder.newJob()
.ofType(CarwlJob.class)
.setJobData(dataMap)
.withIdentity(String.valueOf("task_"+taskId),"group_"+groupId)
.build();
JobKey jobKey= JobKey.jobKey(String.valueOf("task_"+taskId),"group_"+groupId);
if(scheduler.getJobDetail(jobKey)!=null){ //如果该任务存在就替换
scheduler.addJob(jobDetail, true);
}else{
scheduler.scheduleJob(jobDetail, trigger);
}
flag=true;
} catch (SchedulerException e) {
logger.error("---添加任务异常---", e);
}
return flag;
}
/**
* 获取计划中的所有的任务
* @return
* @throws SchedulerException
*/
public static List<ScheduleJob> getScheduleJobs() throws SchedulerException{
List<ScheduleJob> scheduleJobs=new ArrayList<ScheduleJob>();
Scheduler scheduler=getScheduler();
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys=scheduler.getJobKeys(matcher);
for(JobKey jobKey:jobKeys){
List<? extends Trigger> triggers=scheduler.getTriggersOfJob(jobKey);
for(Trigger trigger:triggers){
ScheduleJob scheduleJob=new ScheduleJob();
scheduleJob.setCreateDate(trigger.getStartTime());
scheduleJob.setDescription(trigger.getDescription());
scheduleJob.setJobId(jobKey.getName());
scheduleJob.setGroup(jobKey.getGroup());
scheduleJob.setUrl(scheduler.getJobDetail(jobKey).getJobDataMap().getString("url"));
if(trigger instanceof CronTrigger){
CronTrigger cronTrigger=(CronTrigger) trigger;
scheduleJob.setCronExpression(cronTrigger.getCronExpression()); //获取表达式
}
//当前状态
TriggerState triggerState=scheduler.getTriggerState(trigger.getKey());
scheduleJob.setStatus(triggerState.name());
scheduleJobs.add(scheduleJob);
}
}
return scheduleJobs;
}
/**
* 获取计划中的所有的状态
* @return
* @throws SchedulerException
*/
public static List<Map<String, Object>> getScheduleJobStatuses() throws SchedulerException{
Scheduler scheduler=getScheduler();
List<Map<String, Object>> listStatus=new ArrayList<Map<String, Object>>();
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys=scheduler.getJobKeys(matcher);
for(JobKey jobKey:jobKeys){
List<? extends Trigger> triggers=scheduler.getTriggersOfJob(jobKey);
for(Trigger trigger:triggers){
Map<String, Object> status =new HashMap<String, Object>();
status.put("id", jobKey.getName());
status.put("group",jobKey.getGroup());
TriggerState triggerState=scheduler.getTriggerState(trigger.getKey());
status.put("status", triggerState.name());
listStatus.add(status);
}
}
return listStatus;
}
}
(2) 通过RMI实现Quartz远程控制(此方法用的比较少)
修改quartz.properties 主要属性文件
#==============================================================
#Configure Main Scheduler Properties (客户端与服务端)instanceName 必须一致
#==============================================================
org.quartz.scheduler.instanceName = crawl_scheduler
org.quartz.scheduler.instanceId = AUTO
#==============================================================
#Configure RMI( server ) 用于服务端属性
#==============================================================
#org.quartz.scheduler.rmi.export = true
#org.quartz.scheduler.rmi.createRegistry = true
#org.quartz.scheduler.rmi.registryHost = localhost
#org.quartz.scheduler.rmi.registryPort = 1099
#org.quartz.scheduler.rmi.serverPort = 1100
#==============================================================
#Configure RMI( client ) 客户端调用端属性
#==============================================================
#org.quartz.scheduler.rmi.proxy = true
#org.quartz.scheduler.rmi.registryHost = localhost
#org.quartz.scheduler.rmi.registryPort = 1099
2. quartz 集群的实现;
具体的实现参考(https://www.quartz-scheduler.org/generated/2.2.2/html/qtz-all/#page/quartz-scheduler-webhelp%2Fre-cls_cluster_configuration.html%23)
具体的实现很简单,将所有的定时任务都读取一个数据库 quartz 会自动将任务分配(确保一个任务只有一个执行者);
其实quartz 是通过数据的数据加锁实现 一个任务只有一个执行者,不会出现一个任务多个执行者启动执行的情况;
配置文件一定要有
# 开启集群
org.quartz.jobStore.isClustered = true
#集群的心跳检测
org.quartz.jobStore.clusterCheckinInterval = 20000
3 quartz 一套程序多个实例的实现;
具体参考:http://my.oschina.net/laiweiwei/blog/122280?fromerr=JwLu1rcS
在上面的 SchedulerFactoryManager 中 内部类 TestScheduler 有所实现;
quartz 通过 Scheduler 对象实现管理的;一般此对象为单例的(加载本地quartz.properties 配置文件)实例化对象;
所有可以通过实例化 Scheduler 手动属于属性实现;
{
try {
Properties props = new Properties();
props.put("org.quartz.scheduler.instanceName", "testScheduler");
props.put("org.quartz.threadPool.threadCount", "10");
sf.initialize(props);
} catch (SchedulerException e) {
e.printStackTrace();
}
}