├── README.md
├── easy-job-core
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── rdpaas
│ └── task
│ ├── annotation
│ ├── ContextRefreshedListener.java
│ └── Scheduled.java
│ ├── common
│ ├── Invocation.java
│ ├── Node.java
│ ├── NodeStatus.java
│ ├── NotifyCmd.java
│ ├── Task.java
│ ├── TaskDetail.java
│ └── TaskStatus.java
│ ├── config
│ └── EasyJobConfig.java
│ ├── handles
│ ├── NotifyHandler.java
│ └── StopTaskHandler.java
│ ├── repository
│ ├── NodeRepository.java
│ └── TaskRepository.java
│ ├── scheduler
│ ├── DelayItem.java
│ ├── RecoverExecutor.java
│ └── TaskExecutor.java
│ ├── serializer
│ ├── JdkSerializationSerializer.java
│ └── ObjectSerializer.java
│ ├── strategy
│ ├── DefaultStrategy.java
│ ├── IdHashStrategy.java
│ ├── LeastCountStrategy.java
│ ├── Strategy.java
│ └── WeightStrategy.java
│ └── utils
│ ├── CronExpression.java
│ ├── Delimiters.java
│ └── SpringContextUtil.java
├── easy-job-sample
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── rdpaas
│ │ │ ├── Application.java
│ │ │ └── task
│ │ │ └── sample
│ │ │ └── SchedulerTest.java
│ └── resources
│ │ ├── application.yml
│ │ └── task_scheduling.sql
│ └── test
│ └── java
│ └── com
│ └── rdpaas
│ └── task
│ ├── TaskTest.java
│ └── Test.java
└── pom.xml
/README.md:
--------------------------------------------------------------------------------
1 | # easy-job
2 | 简单的分布式任务调度,详细代码介绍,请看博客:
3 | https://www.cnblogs.com/rongdi/p/10548613.html
4 | https://www.cnblogs.com/rongdi/p/11940402.html
5 |
--------------------------------------------------------------------------------
/easy-job-core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.rdpaas
7 | easy-job-core
8 | 0.0.2-SNAPSHOT
9 | jar
10 |
11 | easy-job-core
12 | easy-job-core
13 |
14 |
15 | com.rdpaas
16 | easy-job-parent
17 | 0.0.2-SNAPSHOT
18 |
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-jdbc
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | src/main/java
34 |
35 | **/*.xml
36 |
37 |
38 |
39 | src/main/resources
40 |
41 | **/*
42 |
43 |
44 |
45 |
46 |
47 | ${project.basedir}/src/test/java
48 |
49 |
50 | ${project.basedir}/src/test/resources
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | org.apache.maven.plugins
59 | maven-compiler-plugin
60 |
61 | ${java.version}
62 | ${java.version}
63 | ${project.build.sourceEncoding}
64 |
65 |
66 |
67 |
68 | org.apache.maven.plugins
69 | maven-javadoc-plugin
70 | 3.0.0
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/annotation/ContextRefreshedListener.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.annotation;
2 |
3 | import com.rdpaas.task.common.Invocation;
4 | import com.rdpaas.task.config.EasyJobConfig;
5 | import com.rdpaas.task.repository.TaskRepository;
6 | import com.rdpaas.task.scheduler.TaskExecutor;
7 | import com.rdpaas.task.utils.Delimiters;
8 | import org.apache.commons.lang3.StringUtils;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.context.ApplicationContext;
11 | import org.springframework.context.ApplicationListener;
12 | import org.springframework.context.event.ContextRefreshedEvent;
13 | import org.springframework.stereotype.Component;
14 |
15 | import java.lang.reflect.Method;
16 | import java.util.Date;
17 | import java.util.HashMap;
18 | import java.util.List;
19 | import java.util.Map;
20 | import java.util.Set;
21 |
22 | /**
23 | * spring容器启动完后,加载自定义注解
24 | * @author rongdi
25 | * @date 2019-03-15 21:07
26 | */
27 | @Component
28 | public class ContextRefreshedListener implements ApplicationListener {
29 |
30 | @Autowired
31 | private TaskExecutor taskExecutor;
32 |
33 | @Autowired
34 | private TaskRepository taskRepository;
35 |
36 | @Autowired
37 | private EasyJobConfig config;
38 | /**
39 | * 用来保存方法名/任务名和任务插入后数据库的ID的映射,用来处理子任务新增用
40 | */
41 | private Map taskIdMap = new HashMap<>();
42 |
43 | /**
44 | * 存放数据库所有的任务名称
45 | */
46 | private List allTaskNames;
47 |
48 |
49 | @Override
50 | public void onApplicationEvent(ContextRefreshedEvent event) {
51 | /**
52 | * 初始化系统启动时间,用于解决系统重启后,还是按照之前时间执行任务
53 | */
54 | config.setSysStartTime(new Date());
55 | /**
56 | * 重启重新初始化本节点的任务状态
57 | */
58 | taskRepository.reInitTasks();
59 | /**
60 | * 查出数据库所有的任务名称
61 | */
62 | allTaskNames = taskRepository.listAllTaskNames();
63 | /**
64 | * 判断根容器为Spring容器,防止出现调用两次的情况(mvc加载也会触发一次)
65 | */
66 | if(event.getApplicationContext().getParent()==null){
67 | /**
68 | * 判断调度开关是否打开
69 | * 如果打开了:加载调度注解并将调度添加到调度管理中
70 | */
71 | ApplicationContext context = event.getApplicationContext();
72 | Map beans = context.getBeansWithAnnotation(org.springframework.scheduling.annotation.EnableScheduling.class);
73 | if(beans == null) {
74 | return;
75 | }
76 | /**
77 | * 用来存放被调度注解修饰的方法名和Method的映射
78 | */
79 | Map methodMap = new HashMap<>();
80 | /**
81 | * 查找所有直接或者间接被Component注解修饰的类,因为不管Service,Controller等都包含了Component,也就是
82 | * 只要是被纳入了spring容器管理的类必然直接或者间接的被Component修饰
83 | */
84 | Map allBeans = context.getBeansWithAnnotation(org.springframework.stereotype.Component.class);
85 | Set> entrys = allBeans.entrySet();
86 | /**
87 | * 遍历bean和里面的method找到被Scheduled注解修饰的方法,然后将任务放入任务调度里
88 | */
89 | for(Map.Entry entry:entrys){
90 | Object obj = entry.getValue();
91 | Class clazz = obj.getClass();
92 | Method[] methods = clazz.getMethods();
93 | for(Method m:methods) {
94 | if(m.isAnnotationPresent(Scheduled.class)) {
95 | methodMap.put(clazz.getName() + Delimiters.DOT + m.getName(),m);
96 | }
97 | }
98 | }
99 | /**
100 | * 处理Sheduled注解
101 | */
102 | handleSheduledAnn(methodMap);
103 | /**
104 | * 由于taskIdMap只是启动spring完成后使用一次,这里可以直接清空
105 | */
106 | taskIdMap.clear();
107 | }
108 | }
109 |
110 | /**
111 | * 循环处理方法map中的所有Method
112 | * @param methodMap
113 | */
114 | private void handleSheduledAnn(Map methodMap) {
115 | if(methodMap == null || methodMap.isEmpty()) {
116 | return;
117 | }
118 | Set> entrys = methodMap.entrySet();
119 | /**
120 | * 遍历bean和里面的method找到被Scheduled注解修饰的方法,然后将任务放入任务调度里
121 | */
122 | for(Map.Entry entry:entrys){
123 | Method m = entry.getValue();
124 | try {
125 | handleSheduledAnn(methodMap,m);
126 | } catch (Exception e) {
127 | e.printStackTrace();
128 | continue;
129 | }
130 | }
131 | }
132 |
133 | /**
134 | * 递归添加父子任务
135 | * @param methodMap
136 | * @param m
137 | * @throws Exception
138 | */
139 | private void handleSheduledAnn(Map methodMap,Method m) throws Exception {
140 | Class> clazz = m.getDeclaringClass();
141 | String name = m.getName();
142 | Scheduled sAnn = m.getAnnotation(Scheduled.class);
143 | String cron = sAnn.cron();
144 | String parent = sAnn.parent();
145 | String finalName = clazz.getName() + Delimiters.DOT + name;
146 | /**
147 | * 如果parent为空,说明该方法代表的任务是根任务,则添加到任务调度器中,并且保存在全局map中
148 | * 如果parent不为空,则表示是子任务,子任务需要知道父任务的id
149 | * 先根据parent里面代表的方法全名或者方法名(父任务方法和子任务方法在同一个类直接可以用方法名,
150 | * 不然要带上类的全名)从taskIdMap获取父任务ID
151 | * 如果找不到父任务ID,先根据父方法全名在methodMap找到父任务的method对象,调用本方法递归下去
152 | * 如果找到父任务ID,则添加子任务
153 | */
154 | if(StringUtils.isEmpty(parent)) {
155 | if(!taskIdMap.containsKey(finalName) && !allTaskNames.contains(finalName)) {
156 | Long taskId = taskExecutor.addTask(finalName, cron, new Invocation(clazz, name, new Class[]{}, new Object[]{}));
157 | taskIdMap.put(finalName, taskId);
158 | }
159 | } else {
160 | String parentMethodName = parent.lastIndexOf(Delimiters.DOT) == -1 ? clazz.getName() + Delimiters.DOT + parent : parent;
161 | Long parentTaskId = taskIdMap.get(parentMethodName);
162 | if(parentTaskId == null) {
163 | Method parentMethod = methodMap.get(parentMethodName);
164 | handleSheduledAnn(methodMap,parentMethod);
165 | /**
166 | * 递归回来一定要更新一下这个父任务ID
167 | */
168 | parentTaskId = taskIdMap.get(parentMethodName);
169 | }
170 | if(parentTaskId != null && !taskIdMap.containsKey(finalName) && !allTaskNames.contains(finalName)) {
171 | Long taskId = taskExecutor.addChildTask(parentTaskId, finalName, cron, new Invocation(clazz, name, new Class[]{}, new Object[]{}));
172 | taskIdMap.put(finalName, taskId);
173 | }
174 |
175 | }
176 |
177 |
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/annotation/Scheduled.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Retention(RetentionPolicy.RUNTIME)
9 | @Target({ ElementType.METHOD })
10 | public @interface Scheduled {
11 | String cron() default "";
12 | String parent() default "";
13 | }
14 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/common/Invocation.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.common;
2 |
3 | import com.rdpaas.task.utils.SpringContextUtil;
4 | import org.springframework.beans.factory.NoSuchBeanDefinitionException;
5 |
6 | import java.io.Serializable;
7 | import java.lang.reflect.Method;
8 |
9 | /**
10 | * 任务执行方法,用于序列化保存在数据库
11 | * @author rongdi
12 | * @date 2019-03-12 19:01
13 | */
14 | public class Invocation implements Serializable {
15 |
16 | private Class targetClass;
17 |
18 | private String methodName;
19 |
20 | private Class[] parameterTypes;
21 |
22 | private Object[] args;
23 |
24 | public Invocation() {
25 |
26 | }
27 |
28 | public Invocation(Class targetClass, String methodName, Class[] parameterTypes, Object... args) {
29 | this.methodName = methodName;
30 | this.parameterTypes = parameterTypes;
31 | this.targetClass = targetClass;
32 | this.args = args;
33 | }
34 |
35 | public Object[] getArgs() {
36 | return args;
37 | }
38 |
39 | public Class getTargetClass() {
40 | return targetClass;
41 | }
42 |
43 | public String getMethodName() {
44 | return methodName;
45 | }
46 |
47 | public Class[] getParameterTypes() {
48 | return parameterTypes;
49 | }
50 |
51 | public Object invoke() throws Exception {
52 | Object target = null;
53 | try {
54 | target = SpringContextUtil.getBean(targetClass);
55 | } catch(NoSuchBeanDefinitionException e) {
56 | target = Class.forName(targetClass.getName());
57 | }
58 | Method method = target.getClass().getMethod(methodName,parameterTypes);
59 | // 调用服务方法
60 | return method.invoke(targetClass.newInstance(), args);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/common/Node.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.common;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * 执行节点对象
7 | * @author rongdi
8 | * @date 2019-03-14 21:12
9 | */
10 | public class Node {
11 |
12 | private Long id;
13 |
14 | /**
15 | * 节点ID,必须唯一
16 | */
17 | private Long nodeId;
18 |
19 | /**
20 | * 节点状态,0表示不可用,1表示可用
21 | */
22 | private NodeStatus status;
23 |
24 | /**
25 | * 节点序号
26 | */
27 | private Long rownum;
28 |
29 | /**
30 | * 执行任务次数
31 | */
32 | private Long counts;
33 |
34 | /**
35 | * 权重,默认都是1
36 | */
37 | private Integer weight = 1;
38 |
39 | /**
40 | * 通知指令
41 | */
42 | private NotifyCmd notifyCmd = NotifyCmd.NO_NOTIFY;
43 |
44 | /**
45 | * 通知值
46 | */
47 | private String notifyValue;
48 |
49 | /**
50 | * 节点创建时间
51 | */
52 | private Date createTime = new Date();
53 |
54 | /**
55 | * 更新时间
56 | */
57 | private Date updateTime = new Date();
58 |
59 | public Node(Long nodeId) {
60 | this.nodeId = nodeId;
61 | }
62 |
63 | public Node() {
64 | }
65 |
66 | public Long getId() {
67 | return id;
68 | }
69 |
70 | public void setId(Long id) {
71 | this.id = id;
72 | }
73 |
74 | public Long getNodeId() {
75 | return nodeId;
76 | }
77 |
78 | public void setNodeId(Long nodeId) {
79 | this.nodeId = nodeId;
80 | }
81 |
82 | public Long getRownum() {
83 | return rownum;
84 | }
85 |
86 | public void setRownum(Long rownum) {
87 | this.rownum = rownum;
88 | }
89 |
90 | public Long getCounts() {
91 | return counts;
92 | }
93 |
94 | public void setCounts(Long counts) {
95 | this.counts = counts;
96 | }
97 |
98 | public Integer getWeight() {
99 | return weight;
100 | }
101 |
102 | public void setWeight(Integer weight) {
103 | this.weight = weight;
104 | }
105 |
106 | public Date getCreateTime() {
107 | return createTime;
108 | }
109 |
110 | public void setCreateTime(Date createTime) {
111 | this.createTime = createTime;
112 | }
113 |
114 | public Date getUpdateTime() {
115 | return updateTime;
116 | }
117 |
118 | public void setUpdateTime(Date updateTime) {
119 | this.updateTime = updateTime;
120 | }
121 |
122 | public NodeStatus getStatus() {
123 | return status;
124 | }
125 |
126 | public void setStatus(int status) {
127 | this.status = NodeStatus.valueOf(status);
128 | }
129 |
130 | public NotifyCmd getNotifyCmd() {
131 | return notifyCmd;
132 | }
133 |
134 | public void setNotifyCmd(int notifyCmd) {
135 | this.notifyCmd = NotifyCmd.valueOf(notifyCmd);
136 | }
137 |
138 | public String getNotifyValue() {
139 | return notifyValue;
140 | }
141 |
142 | public void setNotifyValue(String notifyValue) {
143 | this.notifyValue = notifyValue;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/common/NodeStatus.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.common;
2 |
3 | /**
4 | * 节点状态枚举类
5 | * @author rongdi
6 | * @date 2019-03-14 21:04
7 | */
8 | public enum NodeStatus {
9 |
10 | //待执行
11 | DISABLE(0),
12 | //执行中
13 | ENABLE(1);
14 |
15 | int id;
16 |
17 | NodeStatus(int id) {
18 | this.id = id;
19 | }
20 |
21 | public int getId() {
22 | return id;
23 | }
24 |
25 | public static NodeStatus valueOf(int id) {
26 | switch (id) {
27 | case 1:
28 | return ENABLE;
29 | default:
30 | return DISABLE;
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/common/NotifyCmd.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.common;
2 |
3 | /**
4 | * @author rongdi
5 | * @date 2019/11/26
6 | */
7 | public enum NotifyCmd {
8 |
9 | //没有通知,默认状态
10 | NO_NOTIFY(0),
11 | //开启任务(Task)
12 | START_TASK(1),
13 | //修改任务(Task)
14 | EDIT_TASK(2),
15 | //停止任务(Task)
16 | STOP_TASK(3);
17 |
18 | int id;
19 |
20 | NotifyCmd(int id) {
21 | this.id = id;
22 | }
23 |
24 | public int getId() {
25 | return id;
26 | }
27 |
28 | public static NotifyCmd valueOf(int id) {
29 | switch (id) {
30 | case 1:
31 | return START_TASK;
32 | case 2:
33 | return EDIT_TASK;
34 | case 3:
35 | return STOP_TASK;
36 | default:
37 | return NO_NOTIFY;
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/common/Task.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.common;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * 任务实体
7 | * @author rongdi
8 | * @date 2019-03-12 19:02
9 | */
10 | public class Task {
11 |
12 | private Long id;
13 |
14 | /**
15 | * 父任务id
16 | */
17 | private Long pid;
18 |
19 | /**
20 | * 调度名称
21 | */
22 | private String name;
23 |
24 | /**
25 | * cron表达式
26 | */
27 | private String cronExpr;
28 |
29 | /**
30 | * 当前执行的节点id
31 | */
32 | private Long nodeId;
33 |
34 | /**
35 | * 状态,0表示未开始,1表示待执行,2表示执行中,3表示已完成
36 | */
37 | private TaskStatus status = TaskStatus.NOT_STARTED;
38 |
39 | /**
40 | * 成功次数
41 | */
42 | private Integer successCount;
43 |
44 | /**
45 | * 失败次数
46 | */
47 | private Integer failCount;
48 |
49 | /**
50 | * 执行信息
51 | */
52 | private byte[] invokeInfo;
53 |
54 | /**
55 | * 乐观锁标识
56 | */
57 | private Integer version;
58 |
59 | /**
60 | * 首次开始时间
61 | */
62 | private Date firstStartTime;
63 |
64 | /**
65 | * 下次开始时间
66 | */
67 | private Date nextStartTime;
68 |
69 | /**
70 | * 创建时间
71 | */
72 | private Date createTime = new Date();
73 |
74 | /**
75 | * 更新时间
76 | */
77 | private Date updateTime = new Date();
78 |
79 | /**
80 | * 任务的执行者
81 | */
82 | private Invocation invokor;
83 |
84 | public Task() {
85 | }
86 |
87 | public Task(String name, String cronExpr, Invocation invokor) {
88 | this.name = name;
89 | this.cronExpr = cronExpr;
90 | this.invokor = invokor;
91 | }
92 |
93 | public Long getId() {
94 | return id;
95 | }
96 |
97 | public void setId(Long id) {
98 | this.id = id;
99 | }
100 |
101 | public String getName() {
102 | return name;
103 | }
104 |
105 | public void setName(String name) {
106 | this.name = name;
107 | }
108 |
109 | public Long getNodeId() {
110 | return nodeId;
111 | }
112 |
113 | public void setNodeId(Long nodeId) {
114 | this.nodeId = nodeId;
115 | }
116 |
117 | public Date getFirstStartTime() {
118 | return firstStartTime;
119 | }
120 |
121 | public void setFirstStartTime(Date firstStartTime) {
122 | this.firstStartTime = firstStartTime;
123 | }
124 |
125 | public Date getNextStartTime() {
126 | return nextStartTime;
127 | }
128 |
129 | public void setNextStartTime(Date nextStartTime) {
130 | this.nextStartTime = nextStartTime;
131 | }
132 |
133 | public Long getPid() {
134 | return pid;
135 | }
136 |
137 | public void setPid(Long pid) {
138 | this.pid = pid;
139 | }
140 |
141 | public byte[] getInvokeInfo() {
142 | return invokeInfo;
143 | }
144 |
145 | public void setInvokeInfo(byte[] invokeInfo) {
146 | this.invokeInfo = invokeInfo;
147 | }
148 |
149 | public Invocation getInvokor() {
150 | return invokor;
151 | }
152 |
153 | public void setInvokor(Invocation invokor) {
154 | this.invokor = invokor;
155 | }
156 |
157 | public Integer getSuccessCount() {
158 | return successCount;
159 | }
160 |
161 | public void setSuccessCount(Integer successCount) {
162 | this.successCount = successCount;
163 | }
164 |
165 | public Integer getFailCount() {
166 | return failCount;
167 | }
168 |
169 | public void setFailCount(Integer failCount) {
170 | this.failCount = failCount;
171 | }
172 |
173 | public String getCronExpr() {
174 | return cronExpr;
175 | }
176 |
177 | public void setCronExpr(String cronExpr) {
178 | this.cronExpr = cronExpr;
179 | }
180 |
181 | public TaskStatus getStatus() {
182 | return status;
183 | }
184 |
185 | public void setStatus(TaskStatus status) {
186 | this.status = status;
187 | }
188 |
189 | public Integer getVersion() {
190 | return version;
191 | }
192 |
193 | public void setVersion(Integer version) {
194 | this.version = version;
195 | }
196 |
197 | public Date getCreateTime() {
198 | return createTime;
199 | }
200 |
201 | public void setCreateTime(Date createTime) {
202 | this.createTime = createTime;
203 | }
204 |
205 | public Date getUpdateTime() {
206 | return updateTime;
207 | }
208 |
209 | public void setUpdateTime(Date updateTime) {
210 | this.updateTime = updateTime;
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/common/TaskDetail.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.common;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * 任务实体详情类
7 | * @author rongdi
8 | * @date 2019-03-12 19:03
9 | */
10 | public class TaskDetail {
11 |
12 | private Long id;
13 |
14 | /**
15 | * 任务id
16 | */
17 | private Long taskId;
18 |
19 | /**
20 | * 所属父明细ID
21 | */
22 | private Long pid;
23 |
24 | /**
25 | * 当前执行的节点id
26 | */
27 | private Long nodeId;
28 |
29 | /**
30 | * 重试次数
31 | */
32 | private Integer retryCount;
33 |
34 | /**
35 | * 状态,0表示待执行,1表示执行中,2表示异常中,3表示已完成
36 | * 添加了任务明细说明就开始执行了
37 | */
38 | private TaskStatus status = TaskStatus.DOING;
39 |
40 | /**
41 | * 开始时间
42 | */
43 | private Date startTime = new Date();
44 |
45 | /**
46 | * 结束时间
47 | */
48 | private Date endTime;
49 |
50 | /**
51 | * 乐观锁标识
52 | */
53 | private Integer version;
54 |
55 | /**
56 | * 错误信息
57 | */
58 | private String errorMsg;
59 |
60 | public TaskDetail() {
61 | }
62 |
63 | public TaskDetail(Long taskId) {
64 | this.taskId = taskId;
65 | }
66 |
67 | public Long getId() {
68 | return id;
69 | }
70 |
71 | public void setId(Long id) {
72 | this.id = id;
73 | }
74 |
75 | public Long getTaskId() {
76 | return taskId;
77 | }
78 |
79 | public void setTaskId(Long taskId) {
80 | this.taskId = taskId;
81 | }
82 |
83 | public Long getNodeId() {
84 | return nodeId;
85 | }
86 |
87 | public void setNodeId(Long nodeId) {
88 | this.nodeId = nodeId;
89 | }
90 |
91 | public Integer getRetryCount() {
92 | return retryCount;
93 | }
94 |
95 | public void setRetryCount(Integer retryCount) {
96 | this.retryCount = retryCount;
97 | }
98 |
99 | public TaskStatus getStatus() {
100 | return status;
101 | }
102 |
103 | public void setStatus(TaskStatus status) {
104 | this.status = status;
105 | }
106 |
107 | public Date getStartTime() {
108 | return startTime;
109 | }
110 |
111 | public void setStartTime(Date startTime) {
112 | this.startTime = startTime;
113 | }
114 |
115 | public Date getEndTime() {
116 | return endTime;
117 | }
118 |
119 | public void setEndTime(Date endTime) {
120 | this.endTime = endTime;
121 | }
122 |
123 | public String getErrorMsg() {
124 | return errorMsg;
125 | }
126 |
127 | public void setErrorMsg(String errorMsg) {
128 | this.errorMsg = errorMsg;
129 | }
130 |
131 | public Long getPid() {
132 | return pid;
133 | }
134 |
135 | public void setPid(Long pid) {
136 | this.pid = pid;
137 | }
138 |
139 | public Integer getVersion() {
140 | return version;
141 | }
142 |
143 | public void setVersion(Integer version) {
144 | this.version = version;
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/common/TaskStatus.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.common;
2 |
3 | /**
4 | * 任务状态枚举类
5 | * @author rongdi
6 | * @date 2019-03-12 19:04
7 | */
8 | public enum TaskStatus {
9 |
10 | //未开始
11 | NOT_STARTED(0),
12 | //待执行
13 | PENDING(1),
14 | //执行中
15 | DOING(2),
16 | //异常
17 | ERROR(3),
18 | //已完成
19 | FINISH(4),
20 | //已停止
21 | STOP(5);
22 |
23 | int id;
24 |
25 | TaskStatus(int id) {
26 | this.id = id;
27 | }
28 |
29 | public int getId() {
30 | return id;
31 | }
32 |
33 | public static TaskStatus valueOf(int id) {
34 | switch (id) {
35 | case 1:
36 | return PENDING;
37 | case 2:
38 | return DOING;
39 | case 3:
40 | return ERROR;
41 | case 4:
42 | return FINISH;
43 | case 5:
44 | return STOP;
45 | default:
46 | return NOT_STARTED;
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/config/EasyJobConfig.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.config;
2 |
3 | import org.springframework.beans.factory.annotation.Qualifier;
4 | import org.springframework.beans.factory.annotation.Value;
5 | import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
6 | import org.springframework.boot.context.properties.ConfigurationProperties;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.stereotype.Component;
9 |
10 | import javax.sql.DataSource;
11 | import java.util.Date;
12 |
13 | @Component
14 | public class EasyJobConfig {
15 |
16 | @Value("${easyjob.node.id:1}")
17 | private Long nodeId;
18 |
19 | /**
20 | * 节点取任务的策略
21 | */
22 | @Value("${easyjob.node.strategy:default}")
23 | private String nodeStrategy;
24 |
25 | /**
26 | * 节点取任务的周期,单位是毫秒,默认100毫秒
27 | */
28 | @Value("${easyjob.node.fetchPeriod:100}")
29 | private int fetchPeriod;
30 |
31 | /**
32 | * 节点取任务据当前的时间段,比如每次取还有5分钟开始的任务,这里单位是秒
33 | */
34 | @Value("${easyjob.node.fetchDuration:300}")
35 | private int fetchDuration;
36 |
37 | /**
38 | * 线程池中队列大小
39 | */
40 | @Value("${easyjob.pool.queueSize:1000}")
41 | private int queueSize;
42 |
43 | /**
44 | * 线程池中初始线程数量
45 | */
46 | @Value("${easyjob.pool.coreSize:5}")
47 | private int corePoolSize;
48 |
49 | /**
50 | * 线程池中最大线程数量
51 | */
52 | @Value("${easyjob.pool.maxSize:10}")
53 | private int maxPoolSize;
54 |
55 | /**
56 | * 节点心跳周期,单位秒
57 | */
58 | @Value("${easyjob.heartBeat.seconds:20}")
59 | private int heartBeatSeconds;
60 |
61 | /**
62 | * 节点心跳开关,默认开
63 | */
64 | @Value("${easyjob.heartBeat.enable:true}")
65 | private boolean heartBeatEnable;
66 |
67 | /**
68 | * 恢复线程开关,默认开
69 | */
70 | @Value("${easyjob.recover.enable:true}")
71 | private boolean recoverEnable;
72 |
73 | /**
74 | * 恢复线程周期,默认60s
75 | */
76 | @Value("${easyjob.recover.seconds:60}")
77 | private int recoverSeconds;
78 |
79 | @Bean(name = "easyjobDataSource")
80 | @Qualifier("easyjobDataSource")
81 | @ConfigurationProperties(prefix="easyjob.datasource")
82 | public DataSource primaryDataSource() {
83 | return DataSourceBuilder.create().build();
84 | }
85 |
86 | /**
87 | * 系统启动时间
88 | */
89 | private Date sysStartTime;
90 |
91 | public Long getNodeId() {
92 | return nodeId;
93 | }
94 |
95 | public void setNodeId(Long nodeId) {
96 | this.nodeId = nodeId;
97 | }
98 |
99 | public String getNodeStrategy() {
100 | return nodeStrategy;
101 | }
102 |
103 | public void setNodeStrategy(String nodeStrategy) {
104 | this.nodeStrategy = nodeStrategy;
105 | }
106 |
107 | public int getFetchPeriod() {
108 | return fetchPeriod;
109 | }
110 |
111 | public void setFetchPeriod(int fetchPeriod) {
112 | this.fetchPeriod = fetchPeriod;
113 | }
114 |
115 | public int getFetchDuration() {
116 | return fetchDuration;
117 | }
118 |
119 | public void setFetchDuration(int fetchDuration) {
120 | this.fetchDuration = fetchDuration;
121 | }
122 |
123 | public int getQueueSize() {
124 | return queueSize;
125 | }
126 |
127 | public void setQueueSize(int queueSize) {
128 | this.queueSize = queueSize;
129 | }
130 |
131 | public int getCorePoolSize() {
132 | return corePoolSize;
133 | }
134 |
135 | public void setCorePoolSize(int corePoolSize) {
136 | this.corePoolSize = corePoolSize;
137 | }
138 |
139 | public int getMaxPoolSize() {
140 | return maxPoolSize;
141 | }
142 |
143 | public void setMaxPoolSize(int maxPoolSize) {
144 | this.maxPoolSize = maxPoolSize;
145 | }
146 |
147 | public int getHeartBeatSeconds() {
148 | return heartBeatSeconds;
149 | }
150 |
151 | public void setHeartBeatSeconds(int heartBeatSeconds) {
152 | this.heartBeatSeconds = heartBeatSeconds;
153 | }
154 |
155 | public boolean isHeartBeatEnable() {
156 | return heartBeatEnable;
157 | }
158 |
159 | public void setHeartBeatEnable(boolean heartBeatEnable) {
160 | this.heartBeatEnable = heartBeatEnable;
161 | }
162 |
163 | public boolean isRecoverEnable() {
164 | return recoverEnable;
165 | }
166 |
167 | public void setRecoverEnable(boolean recoverEnable) {
168 | this.recoverEnable = recoverEnable;
169 | }
170 |
171 | public int getRecoverSeconds() {
172 | return recoverSeconds;
173 | }
174 |
175 | public void setRecoverSeconds(int recoverSeconds) {
176 | this.recoverSeconds = recoverSeconds;
177 | }
178 |
179 | public Date getSysStartTime() {
180 | return sysStartTime;
181 | }
182 |
183 | public void setSysStartTime(Date sysStartTime) {
184 | this.sysStartTime = sysStartTime;
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/handles/NotifyHandler.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.handles;
2 |
3 | import com.rdpaas.task.common.NotifyCmd;
4 | import com.rdpaas.task.utils.SpringContextUtil;
5 |
6 | /**
7 | * @author: rongdi
8 | * @date:
9 | */
10 | public interface NotifyHandler {
11 |
12 | static NotifyHandler chooseHandler(NotifyCmd notifyCmd) {
13 | return SpringContextUtil.getByTypeAndName(NotifyHandler.class,notifyCmd.toString());
14 | }
15 |
16 | public void update(T t);
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/handles/StopTaskHandler.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.handles;
2 |
3 | import com.rdpaas.task.scheduler.TaskExecutor;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.stereotype.Component;
6 |
7 | /**
8 | * @author: rongdi
9 | * @date:
10 | */
11 | @Component("STOP_TASK")
12 | public class StopTaskHandler implements NotifyHandler {
13 |
14 | @Autowired
15 | private TaskExecutor taskExecutor;
16 |
17 | @Override
18 | public void update(Long taskId) {
19 | taskExecutor.stop(taskId);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/easy-job-core/src/main/java/com/rdpaas/task/repository/NodeRepository.java:
--------------------------------------------------------------------------------
1 | package com.rdpaas.task.repository;
2 |
3 | import com.rdpaas.task.common.Node;
4 | import com.rdpaas.task.common.NotifyCmd;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Qualifier;
7 | import org.springframework.dao.EmptyResultDataAccessException;
8 | import org.springframework.jdbc.core.BeanPropertyRowMapper;
9 | import org.springframework.jdbc.core.JdbcTemplate;
10 | import org.springframework.jdbc.core.PreparedStatementCreator;
11 | import org.springframework.jdbc.support.GeneratedKeyHolder;
12 | import org.springframework.jdbc.support.KeyHolder;
13 | import org.springframework.stereotype.Component;
14 |
15 | import java.sql.Connection;
16 | import java.sql.PreparedStatement;
17 | import java.sql.SQLException;
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | /**
22 | * 任务对象数据库操作对象
23 | * @author rongdi
24 | * @date 2019-03-12 19:13
25 | */
26 | @Component
27 | public class NodeRepository {
28 |
29 | @Autowired
30 | @Qualifier("easyjobJdbcTemplate")
31 | private JdbcTemplate jdbcTemplate;
32 |
33 | public long insert(Node node) {
34 | String sql = "INSERT INTO easy_job_node(node_id,row_num,weight,notify_cmd,create_time,update_time) VALUES (?, ?, ?, ?, ?, ?);";
35 | KeyHolder kh = new GeneratedKeyHolder();
36 | jdbcTemplate.update(new PreparedStatementCreator() {
37 | @Override
38 | public PreparedStatement createPreparedStatement(Connection con)
39 | throws SQLException {
40 | //设置返回的主键字段名
41 | PreparedStatement ps = con.prepareStatement(sql,new String[]{"id"});
42 | ps.setLong(1,node.getNodeId());
43 | ps.setLong(2,node.getRownum());
44 | ps.setInt(3,node.getWeight());
45 | ps.setInt(4,node.getNotifyCmd().getId());
46 | ps.setTimestamp(5, new java.sql.Timestamp(node.getCreateTime().getTime()));
47 | ps.setTimestamp(6, new java.sql.Timestamp(node.getUpdateTime().getTime()));
48 | return ps;
49 | }
50 | }, kh);
51 | return kh.getKey().longValue();
52 | }
53 |
54 | /**
55 | * 更新节点心跳时间和序号
56 | * @param nodeId 待更新节点ID
57 | * @return
58 | * @throws Exception
59 | */
60 | public int updateHeartBeat(Long nodeId) {
61 | StringBuilder sb = new StringBuilder();
62 | sb.append("update easy_job_node set update_time = now(),row_num = (select tmp.rownum from (")
63 | .append("SELECT (@i:=@i+1) rownum,node_id FROM `easy_job_node`,(SELECT @i:=0) as rownum where status = 1) tmp where tmp.node_id = ?)")
64 | .append("where node_id = ?");
65 | Object objs[] = {nodeId,nodeId};
66 | return jdbcTemplate.update(sb.toString(), objs);
67 | }
68 |
69 | /**
70 | * 更新节点的通知信息,实现修改任务,停止任务通知等
71 | * @param cmd 通知指令
72 | * @param notifyValue 通知的值,一般存id
73 | * @return
74 | */
75 | public int updateNotifyInfo(Long nodeId,NotifyCmd cmd,String notifyValue) {
76 | StringBuilder sb = new StringBuilder();
77 | sb.append("update easy_job_node set notify_cmd = ?,notify_value = ? where node_id = ?");
78 | List