xueli.xue 10 jaren geleden
bovenliggende
commit
328f98a252

+ 5 - 2
README.md Bestand weergeven

@@ -1,2 +1,5 @@
1
-# xxl-job
2
-任务调度框架xxl-job
1
+# 任务调度框架xxl-job
2
+
3
+	Scheduler
4
+	Trigger
5
+	JobDetail

+ 65 - 0
xxl-job-admin/src/test/java/Test.java Bestand weergeven

@@ -0,0 +1,65 @@
1
+import java.util.Date;
2
+import java.util.Timer;
3
+
4
+import org.apache.commons.lang.time.FastDateFormat;
5
+
6
+
7
+public class Test {
8
+	
9
+	static class DemoTimeTask extends java.util.TimerTask {
10
+		public void run() {
11
+			System.out.println("run:" + FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss").format(new Date()));
12
+		}
13
+	} 
14
+	
15
+	// ??? 某一个时间段内,重复执行
16
+	
17
+	// runTime:第一次执行时间
18
+	// delay: 延迟执行的毫秒数,即在delay毫秒之后第一次执行
19
+	// period:重复执行的时间间隔
20
+	public static Timer mainTimer;
21
+	public static void main(String[] args) {
22
+		// 调度器
23
+		mainTimer = new Timer();
24
+		// Demo任务
25
+		DemoTimeTask timeTask = new DemoTimeTask();
26
+		System.out.println("now:" + FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss").format(new Date()));
27
+		
28
+		// 1、在特定时间执行任务,只执行一次
29
+		//Date runTime = DateUtils.addSeconds(new Date(), 1);
30
+		//mainTimer.schedule(timeTask, runTime);				// runTime
31
+		
32
+		// 2、在特定时间之后执行任务,只执行一次
33
+		//long delay = 1000;
34
+		//mainTimer.schedule(timeTask, delay);					// delay/ms
35
+		
36
+		// 3、指定第一次执行的时间,然后按照间隔时间,重复执行
37
+		//Date firstTime = DateUtils.addSeconds(new Date(), 1);
38
+		//long period = 1000;
39
+		//mainTimer.schedule(timeTask, firstTime, period);		// "period/ms" after "firstTime"
40
+		
41
+		// 4、在特定延迟之后第一次执行,然后按照间隔时间,重复执行
42
+		//long delay = 1000;
43
+		//long period = 1000;
44
+		//mainTimer.schedule(timeTask, delay, period);			// "period/ms" after "delay/ms"
45
+		
46
+		// 5、第一次执行之后,特定频率执行,与3同
47
+		//Date firstTime = DateUtils.addSeconds(new Date(), 1);
48
+		//long period = 1000;
49
+		//mainTimer.scheduleAtFixedRate(timeTask, firstTime, period);
50
+		
51
+		// 6、在delay毫秒之后第一次执行,后按照特定频率执行
52
+		long delay = 1000;
53
+		long period = 1000;
54
+		mainTimer.scheduleAtFixedRate(timeTask, delay, period);
55
+		/**
56
+		 * <1>schedule()方法更注重保持间隔时间的稳定:保障每隔period时间可调用一次
57
+		 * <2>scheduleAtFixedRate()方法更注重保持执行频率的稳定:保障多次调用的频率趋近于period时间,如果任务执行时间大于period,会在任务执行之后马上执行下一次任务
58
+		 */
59
+
60
+		// Timer注销
61
+		mainTimer.cancel();
62
+		
63
+	}
64
+	 
65
+}

+ 2 - 0
xxl-job-demo/pom.xml Bestand weergeven

@@ -135,6 +135,8 @@
135 135
 			<version>0.0.1-SNAPSHOT</version>
136 136
 		</dependency>
137 137
 
138
+
139
+
138 140
 	</dependencies>
139 141
 
140 142
 	<build>

+ 90 - 0
xxl-job-demo/src/main/java/com/xxl/quartz/DynamicSchedulerUtil.java Bestand weergeven

@@ -0,0 +1,90 @@
1
+package com.xxl.quartz;
2
+
3
+import org.quartz.*;
4
+import org.slf4j.Logger;
5
+import org.slf4j.LoggerFactory;
6
+import org.springframework.beans.factory.InitializingBean;
7
+import org.springframework.util.Assert;
8
+
9
+import java.util.Date;
10
+
11
+public final class DynamicSchedulerUtil implements InitializingBean {
12
+    private static final Logger logger = LoggerFactory.getLogger(DynamicSchedulerUtil.class);
13
+    
14
+    // Scheduler
15
+    private static Scheduler scheduler;
16
+    public static void setScheduler(Scheduler scheduler) {
17
+		DynamicSchedulerUtil.scheduler = scheduler;
18
+	}
19
+
20
+	@Override
21
+    public void afterPropertiesSet() throws Exception {
22
+        Assert.notNull(scheduler, "quartz scheduler is null");
23
+        logger.info(">>>>>>>>> init quartz scheduler success.[{}]", scheduler);
24
+    }
25
+
26
+	// Add 新增
27
+    public static boolean addJob(JobModel job) throws SchedulerException {
28
+        final TriggerKey triggerKey = job.getTriggerKey();
29
+        if (scheduler.checkExists(triggerKey)) {
30
+            final Trigger trigger = scheduler.getTrigger(triggerKey);
31
+            logger.info(">>>>>>>>> Already exist trigger [" + trigger + "] by key [" + triggerKey + "] in Scheduler");
32
+            return false;
33
+        }
34
+
35
+        final CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
36
+        final CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
37
+                .withSchedule(cronScheduleBuilder)
38
+                .build();
39
+
40
+        final JobDetail jobDetail = job.getJobDetail();
41
+        final Date date = scheduler.scheduleJob(jobDetail, cronTrigger);
42
+
43
+        logger.debug("Register DynamicJob {} on [{}]", job, date);
44
+        return true;
45
+    }
46
+
47
+    // Pause 暂停-指定Job
48
+    public static boolean pauseJob(JobModel existJob) throws SchedulerException {
49
+        final TriggerKey triggerKey = existJob.getTriggerKey();
50
+        boolean result = false;
51
+        if (scheduler.checkExists(triggerKey)) {
52
+            scheduler.pauseTrigger(triggerKey);
53
+            result = true;
54
+            logger.debug("Pause exist DynamicJob {}, triggerKey [{}] successful", existJob, triggerKey);
55
+        } else {
56
+            logger.debug("Failed pause exist DynamicJob {}, because not fount triggerKey [{}]", existJob, triggerKey);
57
+        }
58
+        return result;
59
+    }
60
+
61
+    // Resume 重启-指定Job
62
+    public static boolean resumeJob(JobModel existJob) throws SchedulerException {
63
+        final TriggerKey triggerKey = existJob.getTriggerKey();
64
+        boolean result = false;
65
+        if (scheduler.checkExists(triggerKey)) {
66
+            final CronTrigger newTrigger = existJob.cronTrigger();
67
+            final Date date = scheduler.rescheduleJob(triggerKey, newTrigger);
68
+
69
+            result = true;
70
+            logger.debug("Resume exist DynamicJob {}, triggerKey [{}] on [{}] successful", existJob, triggerKey, date);
71
+        } else {
72
+            logger.debug("Failed resume exist DynamicJob {}, because not fount triggerKey [{}]", existJob, triggerKey);
73
+        }
74
+        return result;
75
+    }
76
+
77
+    // Remove exists job 移除-指定Job
78
+    public static boolean removeJob(JobModel existJob) throws SchedulerException {
79
+        final TriggerKey triggerKey = existJob.getTriggerKey();
80
+        boolean result = false;
81
+        if (scheduler.checkExists(triggerKey)) {
82
+            result = scheduler.unscheduleJob(triggerKey);
83
+        }
84
+
85
+        logger.debug("Remove DynamicJob {} result [{}]", existJob, result);
86
+        return result;
87
+    }
88
+
89
+
90
+}

+ 77 - 0
xxl-job-demo/src/main/java/com/xxl/quartz/JobModel.java Bestand weergeven

@@ -0,0 +1,77 @@
1
+package com.xxl.quartz;
2
+
3
+import org.quartz.CronScheduleBuilder;
4
+import org.quartz.CronTrigger;
5
+import org.quartz.Job;
6
+import org.quartz.JobBuilder;
7
+import org.quartz.JobDataMap;
8
+import org.quartz.JobDetail;
9
+import org.quartz.Scheduler;
10
+import org.quartz.TriggerBuilder;
11
+import org.quartz.TriggerKey;
12
+
13
+/**
14
+ * 任务model
15
+ * @author xuxueli 2015-12-1 16:01:19
16
+ */
17
+public class JobModel {
18
+	
19
+	// param
20
+	private String group;
21
+	private String name;
22
+	private String cronExpression;
23
+	private Class<? extends Job> jobClass;
24
+	
25
+    public JobModel(String name, String cronExpression, Class<? extends Job> jobClass) {
26
+		this.group = Scheduler.DEFAULT_GROUP;
27
+		this.name = name;
28
+		this.cronExpression = cronExpression;
29
+		this.jobClass = jobClass;
30
+	}
31
+    
32
+	public String getGroup() {
33
+		return group;
34
+	}
35
+	public void setGroup(String group) {
36
+		this.group = group;
37
+	}
38
+	public String getName() {
39
+		return name;
40
+	}
41
+	public void setName(String name) {
42
+		this.name = name;
43
+	}
44
+	public String getCronExpression() {
45
+		return cronExpression;
46
+	}
47
+	public void setCronExpression(String cronExpression) {
48
+		this.cronExpression = cronExpression;
49
+	}
50
+	public Class<? extends Job> getJobClass() {
51
+		return jobClass;
52
+	}
53
+	public void setJobClass(Class<? extends Job> jobClass) {
54
+		this.jobClass = jobClass;
55
+	}
56
+	
57
+	// TriggerKey
58
+	public TriggerKey getTriggerKey() {
59
+		return TriggerKey.triggerKey(this.name, this.group);
60
+	}
61
+	// JobDetail
62
+	public JobDetail getJobDetail() {
63
+		return JobBuilder.newJob(jobClass).withIdentity(this.name, this.group).build();
64
+	}
65
+	// JobDataMap.add
66
+    public JobModel addJobData(String key, Object value) {
67
+        JobDataMap jobDataMap = this.getJobDetail().getJobDataMap();
68
+        jobDataMap.put(key, value);
69
+        return this;
70
+    }
71
+    // CronTrigger
72
+    public CronTrigger cronTrigger() {
73
+        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(this.cronExpression);
74
+        return TriggerBuilder.newTrigger().withIdentity(this.getTriggerKey()).withSchedule(cronScheduleBuilder).build();
75
+    }
76
+
77
+}

+ 4 - 6
xxl-job-demo/src/main/java/com/xxl/service/impl/TriggerServiceImpl.java Bestand weergeven

@@ -1,5 +1,8 @@
1 1
 package com.xxl.service.impl;
2 2
 
3
+import java.util.Date;
4
+
5
+import org.apache.commons.lang.time.FastDateFormat;
3 6
 import org.slf4j.Logger;
4 7
 import org.slf4j.LoggerFactory;
5 8
 import org.springframework.stereotype.Service;
@@ -19,12 +22,7 @@ public class TriggerServiceImpl implements ITriggerService {
19 22
 	 * 全站静态化
20 23
 	 */
21 24
 	public void generateNetHtml() {
22
-		long start = System.currentTimeMillis();
23
-		logger.info("全站静态化... start:{}", start);
24
-		
25
-			
26
-		long end = System.currentTimeMillis();
27
-		logger.info("全站静态化... end:{}, cost:{}", end, end - start);
25
+		logger.info("全站静态化 run at :{}", FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss").format(new Date()));
28 26
 	}
29 27
 	
30 28
 }

+ 21 - 0
xxl-job-demo/src/main/java/com/xxl/service/job/JobDetailDemo.java Bestand weergeven

@@ -0,0 +1,21 @@
1
+package com.xxl.service.job;
2
+
3
+import java.util.Date;
4
+
5
+import org.apache.commons.lang.time.FastDateFormat;
6
+import org.quartz.JobExecutionContext;
7
+import org.quartz.JobExecutionException;
8
+import org.slf4j.Logger;
9
+import org.slf4j.LoggerFactory;
10
+import org.springframework.scheduling.quartz.QuartzJobBean;
11
+
12
+public class JobDetailDemo extends QuartzJobBean {
13
+	private static Logger logger = LoggerFactory.getLogger(JobDetailDemo.class);
14
+	
15
+	@Override
16
+	protected void executeInternal(JobExecutionContext context)
17
+			throws JobExecutionException {
18
+		logger.info("全站静态化[DB] run at :{}", FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss").format(new Date()));
19
+	}
20
+
21
+}

+ 46 - 0
xxl-job-demo/src/main/resources/applicationcontext-trigger-db.xml Bestand weergeven

@@ -0,0 +1,46 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<beans xmlns="http://www.springframework.org/schema/beans"
3
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
4
+	xmlns:util="http://www.springframework.org/schema/util"
5
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
6
+           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
7
+           http://www.springframework.org/schema/context
8
+           http://www.springframework.org/schema/context/spring-context-3.0.xsd
9
+           http://www.springframework.org/schema/util 
10
+           http://www.springframework.org/schema/util/spring-util.xsd">
11
+
12
+	<!-- Job trigger -->
13
+	<bean id="job02Trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
14
+		<property name="jobDetail" >
15
+			<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
16
+				<property name="jobClass" value="com.xxl.service.job.JobDetailDemo"/>
17
+				<property name="jobDataAsMap">
18
+					<map>
19
+						<!-- <entry key="xxService" value-ref="xxService" /> -->
20
+					</map>
21
+				</property>  
22
+				<property name="durability" value="true" />
23
+			</bean>
24
+		</property>
25
+		<property name="cronExpression" value="0/3 * * * * ? *" />
26
+	</bean>
27
+
28
+	<!-- Job信息会被上报并持久化到mysql中,多个集群节点中竞争Job锁,只会有一台执行 -->
29
+	<bean id="quartzScheduler" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
30
+		<property name="dataSource" ref="dataSource" />
31
+        <property name="autoStartup" value="true" />
32
+		<property name="applicationContextSchedulerContextKey"  value="applicationContextKey" /> 
33
+        <property name="configLocation" value="classpath:quartz.properties"/>
34
+		<property name="triggers">
35
+			<list>
36
+				<!-- <ref bean="job02Trigger" /> -->
37
+			</list>
38
+		</property>
39
+	</bean>
40
+	
41
+	<!-- 调度器 -->
42
+	<bean id="dynamicSchedulerUtil" class="com.xxl.quartz.DynamicSchedulerUtil">
43
+        <property name="scheduler" ref="quartzScheduler"/>
44
+    </bean>
45
+
46
+</beans>

xxl-job-demo/src/main/resources/applicationcontext-trigger.xml → xxl-job-demo/src/main/resources/applicationcontext-trigger-local.xml Bestand weergeven

@@ -22,7 +22,7 @@
22 22
 		<property name="cronExpression" value="0/3 * * * * ? *" />
23 23
 	</bean>
24 24
 	
25
-	<!-- 启动触发器的配置开始 -->
25
+	<!-- Job被加载到内存中,每个集群节点相互独立,都会执行该任务 -->
26 26
 	<bean name="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
27 27
 		<property name="triggers">
28 28
 			<list>

+ 24 - 0
xxl-job-demo/src/main/resources/quartz.properties Bestand weergeven

@@ -0,0 +1,24 @@
1
+# Default Properties file for use by StdSchedulerFactory
2
+# to create a Quartz Scheduler Instance, if a different
3
+# properties file is not explicitly specified.
4
+#
5
+
6
+org.quartz.scheduler.instanceName: DefaultQuartzScheduler
7
+org.quartz.scheduler.rmi.export: false
8
+org.quartz.scheduler.rmi.proxy: false
9
+org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
10
+
11
+org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
12
+org.quartz.threadPool.threadCount: 10
13
+org.quartz.threadPool.threadPriority: 5
14
+org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
15
+
16
+org.quartz.jobStore.misfireThreshold: 60000
17
+
18
+#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
19
+
20
+# for cluster
21
+org.quartz.scheduler.instanceId: AUTO
22
+org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
23
+org.quartz.jobStore.isClustered: true
24
+org.quartz.jobStore.clusterCheckinInterval: 1000

+ 30 - 0
xxl-job-demo/src/test/java/quartz/JunitTest.java Bestand weergeven

@@ -0,0 +1,30 @@
1
+package quartz;
2
+
3
+
4
+import java.lang.reflect.InvocationTargetException;
5
+import java.util.concurrent.TimeUnit;
6
+
7
+import org.junit.Test;
8
+import org.junit.runner.RunWith;
9
+import org.quartz.SchedulerException;
10
+import org.springframework.test.context.ContextConfiguration;
11
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12
+
13
+import com.xxl.quartz.DynamicSchedulerUtil;
14
+import com.xxl.quartz.JobModel;
15
+
16
+@RunWith(SpringJUnit4ClassRunner.class)
17
+@ContextConfiguration(locations = "classpath*:applicationcontext-*.xml")
18
+public class JunitTest {
19
+	
20
+    @Test
21
+    public void addJob() throws SchedulerException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InterruptedException {
22
+    	boolean ret = DynamicSchedulerUtil.addJob(new JobModel("Jost-job", "0/1 * * * * ?", TestDynamicJob.class));
23
+    	System.out.println(ret);
24
+    	TimeUnit.SECONDS.sleep(30);
25
+    }
26
+    
27
+    
28
+    
29
+    
30
+}

+ 17 - 0
xxl-job-demo/src/test/java/quartz/TestDynamicJob.java Bestand weergeven

@@ -0,0 +1,17 @@
1
+package quartz;
2
+
3
+import org.quartz.Job;
4
+import org.quartz.JobExecutionContext;
5
+import org.quartz.JobExecutionException;
6
+
7
+import java.util.Date;
8
+
9
+public class TestDynamicJob implements Job {
10
+
11
+
12
+    @Override
13
+    public void execute(JobExecutionContext context) throws JobExecutionException {
14
+        final Object mailGuid = context.getMergedJobDataMap().get("mailGuid");
15
+        System.out.println("[Dynamic-Job]  It is " + new Date() + " now, mailGuid=" + mailGuid);
16
+    }
17
+}