parent
08950b0b6c
commit
328f98a252
12 changed files with 382 additions and 9 deletions
@ -1,2 +1,5 @@ |
||||
# xxl-job |
||||
任务调度框架xxl-job |
||||
# 任务调度框架xxl-job |
||||
|
||||
Scheduler |
||||
Trigger |
||||
JobDetail |
||||
|
@ -0,0 +1,65 @@ |
||||
import java.util.Date; |
||||
import java.util.Timer; |
||||
|
||||
import org.apache.commons.lang.time.FastDateFormat; |
||||
|
||||
|
||||
public class Test { |
||||
|
||||
static class DemoTimeTask extends java.util.TimerTask { |
||||
public void run() { |
||||
System.out.println("run:" + FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss").format(new Date())); |
||||
} |
||||
} |
||||
|
||||
// ??? 某一个时间段内,重复执行
|
||||
|
||||
// runTime:第一次执行时间
|
||||
// delay: 延迟执行的毫秒数,即在delay毫秒之后第一次执行
|
||||
// period:重复执行的时间间隔
|
||||
public static Timer mainTimer; |
||||
public static void main(String[] args) { |
||||
// 调度器
|
||||
mainTimer = new Timer(); |
||||
// Demo任务
|
||||
DemoTimeTask timeTask = new DemoTimeTask(); |
||||
System.out.println("now:" + FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss").format(new Date())); |
||||
|
||||
// 1、在特定时间执行任务,只执行一次
|
||||
//Date runTime = DateUtils.addSeconds(new Date(), 1);
|
||||
//mainTimer.schedule(timeTask, runTime); // runTime
|
||||
|
||||
// 2、在特定时间之后执行任务,只执行一次
|
||||
//long delay = 1000;
|
||||
//mainTimer.schedule(timeTask, delay); // delay/ms
|
||||
|
||||
// 3、指定第一次执行的时间,然后按照间隔时间,重复执行
|
||||
//Date firstTime = DateUtils.addSeconds(new Date(), 1);
|
||||
//long period = 1000;
|
||||
//mainTimer.schedule(timeTask, firstTime, period); // "period/ms" after "firstTime"
|
||||
|
||||
// 4、在特定延迟之后第一次执行,然后按照间隔时间,重复执行
|
||||
//long delay = 1000;
|
||||
//long period = 1000;
|
||||
//mainTimer.schedule(timeTask, delay, period); // "period/ms" after "delay/ms"
|
||||
|
||||
// 5、第一次执行之后,特定频率执行,与3同
|
||||
//Date firstTime = DateUtils.addSeconds(new Date(), 1);
|
||||
//long period = 1000;
|
||||
//mainTimer.scheduleAtFixedRate(timeTask, firstTime, period);
|
||||
|
||||
// 6、在delay毫秒之后第一次执行,后按照特定频率执行
|
||||
long delay = 1000; |
||||
long period = 1000; |
||||
mainTimer.scheduleAtFixedRate(timeTask, delay, period); |
||||
/** |
||||
* <1>schedule()方法更注重保持间隔时间的稳定:保障每隔period时间可调用一次 |
||||
* <2>scheduleAtFixedRate()方法更注重保持执行频率的稳定:保障多次调用的频率趋近于period时间,如果任务执行时间大于period,会在任务执行之后马上执行下一次任务 |
||||
*/ |
||||
|
||||
// Timer注销
|
||||
mainTimer.cancel(); |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,90 @@ |
||||
package com.xxl.quartz; |
||||
|
||||
import org.quartz.*; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.beans.factory.InitializingBean; |
||||
import org.springframework.util.Assert; |
||||
|
||||
import java.util.Date; |
||||
|
||||
public final class DynamicSchedulerUtil implements InitializingBean { |
||||
private static final Logger logger = LoggerFactory.getLogger(DynamicSchedulerUtil.class); |
||||
|
||||
// Scheduler
|
||||
private static Scheduler scheduler; |
||||
public static void setScheduler(Scheduler scheduler) { |
||||
DynamicSchedulerUtil.scheduler = scheduler; |
||||
} |
||||
|
||||
@Override |
||||
public void afterPropertiesSet() throws Exception { |
||||
Assert.notNull(scheduler, "quartz scheduler is null"); |
||||
logger.info(">>>>>>>>> init quartz scheduler success.[{}]", scheduler); |
||||
} |
||||
|
||||
// Add 新增
|
||||
public static boolean addJob(JobModel job) throws SchedulerException { |
||||
final TriggerKey triggerKey = job.getTriggerKey(); |
||||
if (scheduler.checkExists(triggerKey)) { |
||||
final Trigger trigger = scheduler.getTrigger(triggerKey); |
||||
logger.info(">>>>>>>>> Already exist trigger [" + trigger + "] by key [" + triggerKey + "] in Scheduler"); |
||||
return false; |
||||
} |
||||
|
||||
final CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); |
||||
final CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey) |
||||
.withSchedule(cronScheduleBuilder) |
||||
.build(); |
||||
|
||||
final JobDetail jobDetail = job.getJobDetail(); |
||||
final Date date = scheduler.scheduleJob(jobDetail, cronTrigger); |
||||
|
||||
logger.debug("Register DynamicJob {} on [{}]", job, date); |
||||
return true; |
||||
} |
||||
|
||||
// Pause 暂停-指定Job
|
||||
public static boolean pauseJob(JobModel existJob) throws SchedulerException { |
||||
final TriggerKey triggerKey = existJob.getTriggerKey(); |
||||
boolean result = false; |
||||
if (scheduler.checkExists(triggerKey)) { |
||||
scheduler.pauseTrigger(triggerKey); |
||||
result = true; |
||||
logger.debug("Pause exist DynamicJob {}, triggerKey [{}] successful", existJob, triggerKey); |
||||
} else { |
||||
logger.debug("Failed pause exist DynamicJob {}, because not fount triggerKey [{}]", existJob, triggerKey); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
// Resume 重启-指定Job
|
||||
public static boolean resumeJob(JobModel existJob) throws SchedulerException { |
||||
final TriggerKey triggerKey = existJob.getTriggerKey(); |
||||
boolean result = false; |
||||
if (scheduler.checkExists(triggerKey)) { |
||||
final CronTrigger newTrigger = existJob.cronTrigger(); |
||||
final Date date = scheduler.rescheduleJob(triggerKey, newTrigger); |
||||
|
||||
result = true; |
||||
logger.debug("Resume exist DynamicJob {}, triggerKey [{}] on [{}] successful", existJob, triggerKey, date); |
||||
} else { |
||||
logger.debug("Failed resume exist DynamicJob {}, because not fount triggerKey [{}]", existJob, triggerKey); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
// Remove exists job 移除-指定Job
|
||||
public static boolean removeJob(JobModel existJob) throws SchedulerException { |
||||
final TriggerKey triggerKey = existJob.getTriggerKey(); |
||||
boolean result = false; |
||||
if (scheduler.checkExists(triggerKey)) { |
||||
result = scheduler.unscheduleJob(triggerKey); |
||||
} |
||||
|
||||
logger.debug("Remove DynamicJob {} result [{}]", existJob, result); |
||||
return result; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,77 @@ |
||||
package com.xxl.quartz; |
||||
|
||||
import org.quartz.CronScheduleBuilder; |
||||
import org.quartz.CronTrigger; |
||||
import org.quartz.Job; |
||||
import org.quartz.JobBuilder; |
||||
import org.quartz.JobDataMap; |
||||
import org.quartz.JobDetail; |
||||
import org.quartz.Scheduler; |
||||
import org.quartz.TriggerBuilder; |
||||
import org.quartz.TriggerKey; |
||||
|
||||
/** |
||||
* 任务model |
||||
* @author xuxueli 2015-12-1 16:01:19 |
||||
*/ |
||||
public class JobModel { |
||||
|
||||
// param
|
||||
private String group; |
||||
private String name; |
||||
private String cronExpression; |
||||
private Class<? extends Job> jobClass; |
||||
|
||||
public JobModel(String name, String cronExpression, Class<? extends Job> jobClass) { |
||||
this.group = Scheduler.DEFAULT_GROUP; |
||||
this.name = name; |
||||
this.cronExpression = cronExpression; |
||||
this.jobClass = jobClass; |
||||
} |
||||
|
||||
public String getGroup() { |
||||
return group; |
||||
} |
||||
public void setGroup(String group) { |
||||
this.group = group; |
||||
} |
||||
public String getName() { |
||||
return name; |
||||
} |
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
public String getCronExpression() { |
||||
return cronExpression; |
||||
} |
||||
public void setCronExpression(String cronExpression) { |
||||
this.cronExpression = cronExpression; |
||||
} |
||||
public Class<? extends Job> getJobClass() { |
||||
return jobClass; |
||||
} |
||||
public void setJobClass(Class<? extends Job> jobClass) { |
||||
this.jobClass = jobClass; |
||||
} |
||||
|
||||
// TriggerKey
|
||||
public TriggerKey getTriggerKey() { |
||||
return TriggerKey.triggerKey(this.name, this.group); |
||||
} |
||||
// JobDetail
|
||||
public JobDetail getJobDetail() { |
||||
return JobBuilder.newJob(jobClass).withIdentity(this.name, this.group).build(); |
||||
} |
||||
// JobDataMap.add
|
||||
public JobModel addJobData(String key, Object value) { |
||||
JobDataMap jobDataMap = this.getJobDetail().getJobDataMap(); |
||||
jobDataMap.put(key, value); |
||||
return this; |
||||
} |
||||
// CronTrigger
|
||||
public CronTrigger cronTrigger() { |
||||
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(this.cronExpression); |
||||
return TriggerBuilder.newTrigger().withIdentity(this.getTriggerKey()).withSchedule(cronScheduleBuilder).build(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,21 @@ |
||||
package com.xxl.service.job; |
||||
|
||||
import java.util.Date; |
||||
|
||||
import org.apache.commons.lang.time.FastDateFormat; |
||||
import org.quartz.JobExecutionContext; |
||||
import org.quartz.JobExecutionException; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.scheduling.quartz.QuartzJobBean; |
||||
|
||||
public class JobDetailDemo extends QuartzJobBean { |
||||
private static Logger logger = LoggerFactory.getLogger(JobDetailDemo.class); |
||||
|
||||
@Override |
||||
protected void executeInternal(JobExecutionContext context) |
||||
throws JobExecutionException { |
||||
logger.info("全站静态化[DB] run at :{}", FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss").format(new Date())); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,46 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" |
||||
xmlns:util="http://www.springframework.org/schema/util" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans |
||||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
||||
http://www.springframework.org/schema/context |
||||
http://www.springframework.org/schema/context/spring-context-3.0.xsd |
||||
http://www.springframework.org/schema/util |
||||
http://www.springframework.org/schema/util/spring-util.xsd"> |
||||
|
||||
<!-- Job trigger --> |
||||
<bean id="job02Trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> |
||||
<property name="jobDetail" > |
||||
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> |
||||
<property name="jobClass" value="com.xxl.service.job.JobDetailDemo"/> |
||||
<property name="jobDataAsMap"> |
||||
<map> |
||||
<!-- <entry key="xxService" value-ref="xxService" /> --> |
||||
</map> |
||||
</property> |
||||
<property name="durability" value="true" /> |
||||
</bean> |
||||
</property> |
||||
<property name="cronExpression" value="0/3 * * * * ? *" /> |
||||
</bean> |
||||
|
||||
<!-- Job信息会被上报并持久化到mysql中,多个集群节点中竞争Job锁,只会有一台执行 --> |
||||
<bean id="quartzScheduler" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> |
||||
<property name="dataSource" ref="dataSource" /> |
||||
<property name="autoStartup" value="true" /> |
||||
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" /> |
||||
<property name="configLocation" value="classpath:quartz.properties"/> |
||||
<property name="triggers"> |
||||
<list> |
||||
<!-- <ref bean="job02Trigger" /> --> |
||||
</list> |
||||
</property> |
||||
</bean> |
||||
|
||||
<!-- 调度器 --> |
||||
<bean id="dynamicSchedulerUtil" class="com.xxl.quartz.DynamicSchedulerUtil"> |
||||
<property name="scheduler" ref="quartzScheduler"/> |
||||
</bean> |
||||
|
||||
</beans> |
@ -0,0 +1,24 @@ |
||||
# 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 |
||||
|
||||
# for cluster |
||||
org.quartz.scheduler.instanceId: AUTO |
||||
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX |
||||
org.quartz.jobStore.isClustered: true |
||||
org.quartz.jobStore.clusterCheckinInterval: 1000 |
@ -0,0 +1,30 @@ |
||||
package quartz; |
||||
|
||||
|
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.quartz.SchedulerException; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; |
||||
|
||||
import com.xxl.quartz.DynamicSchedulerUtil; |
||||
import com.xxl.quartz.JobModel; |
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class) |
||||
@ContextConfiguration(locations = "classpath*:applicationcontext-*.xml") |
||||
public class JunitTest { |
||||
|
||||
@Test |
||||
public void addJob() throws SchedulerException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InterruptedException { |
||||
boolean ret = DynamicSchedulerUtil.addJob(new JobModel("Jost-job", "0/1 * * * * ?", TestDynamicJob.class)); |
||||
System.out.println(ret); |
||||
TimeUnit.SECONDS.sleep(30); |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
} |
@ -0,0 +1,17 @@ |
||||
package quartz; |
||||
|
||||
import org.quartz.Job; |
||||
import org.quartz.JobExecutionContext; |
||||
import org.quartz.JobExecutionException; |
||||
|
||||
import java.util.Date; |
||||
|
||||
public class TestDynamicJob implements Job { |
||||
|
||||
|
||||
@Override |
||||
public void execute(JobExecutionContext context) throws JobExecutionException { |
||||
final Object mailGuid = context.getMergedJobDataMap().get("mailGuid"); |
||||
System.out.println("[Dynamic-Job] It is " + new Date() + " now, mailGuid=" + mailGuid); |
||||
} |
||||
} |
Loading…
Reference in new issue