实现一个并发任务执行框架。
问题参考来自网络
一、需求产生和分析
公司里有两个项目组,考试组有批量的离线文档要生成,题库组则经常有批量的题目进行排重和根据条件批量修改题目的内容。
架构组通过对实际的上线产品进行用户调查,发现这些功能在实际使用时,用户都反应速度很慢,而且提交任务后,不知道任务的进行情况,做没做?做到哪一步了?有哪些成功?哪些失败了?都一概不知道架构组和实际的开发人员沟通,他们都说,因为前端提交任务到 Web 后台以后,是一次要处理多个文档和题目,所以速度快不起来。提示用多线程进行改进,实际的开发人员表示多线程没有用过,不知道如何使用,也担心用不好。综合以上情况,架构组决定在公司的基础构件库中提供一个并发任务执行框架,以解决上述用户和业务开发人员的痛点:
1、对批量型任务提供统一的开发接口。
2、在使用上尽可能的对业务开发人员友好。
3、要求可以查询批量任务的执行进度。
二、需要怎么做
1、批量任务,为提高性能,必然的我们要使用 java 里的多线程,为了在使用上尽可能的对业务开发人员友好和简单,需要屏蔽一些底层 java 并发编程中的细节,让他们不需要去了解并发容器,阻塞队列,异步任务,线程安全等等方面的知识,只要专心于自的业务处理即可。
2、每个批量任务拥有自己的上下文环境,因为一个项目组里同时要处理的批量任务可能有多个,比如考试组,可能就会有不同的学校的批量的离线文档生成,而题库组则会不同的学科都会有老师同时进行工作,因此需要一个并发安全的容器保存每个任务的属性信息。
3、自动清除已完成和过期任务因为要提供进度查询,系统需要在内存中维护每个任务的进度信息以供查询,但是这种查询又是有时间限制的,一个任务完成一段时间后,就不再提供进度查询了,则就需要我们自动清除已完成和过期任务,用轮询来处理的话不太优雅,那么就可以使用一个 DelayQueue 或者 Redis了。
三、实现流程图
四、具体实现代码
/**
* @Author SunnyBear
* @Description 方法本身运行是否正确的结果类型
*/
public enum TaskResultType {
/**
* 方法执行完成,业务结果也正确
*/
SUCCESS,
/**
* 方法执行完成,业务结果错误
*/
FAILURE,
/**
* 方法执行抛出异常
*/
EXCEPTION
}
/**
* @Author SunnyBear
* @Description 任务处理后返回的结果实体类
*/
public class TaskResult<R> {
/**
* 方法执行结果
*/
private final TaskResultType taskResultType;
/**
* 方法执行后的结果数据
*/
private final R returnValue;
/**
* 如果方法执行失败,可以在这里填充原因
*/
private final String reason;
public TaskResult(TaskResultType taskResultType, R returnValue, String reason) {
this.taskResultType = taskResultType;
this.returnValue = returnValue;
this.reason = reason;
}
public TaskResultType getTaskResultType() {
return taskResultType;
}
public R getReturnValue() {
return returnValue;
}
public String getReason() {
return reason;
}
@Override
public String toString() {
return "TaskResult{" +
"taskResultType=" + taskResultType +
", returnValue=" + returnValue +
", reason='" + reason + '\'' +
'}';
}
}
/**
* @Author SunnyBear
* @Description 要求框架使用者实现的任务接口,因为任务的性质在调用时才知道,所以传入的参数(T)和方法(R)的返回值均使用泛型
*/
public interface ITaskProcesser<T, R> {
TaskResult<R> taskExecute(T data);
}
/**
* @Author SunnyBear
* @Description 存放的队列的元素
*/
public class Item<T> implements Delayed {
/**
* 到期时间(秒)
*/
private long activeTime;
/**
* 业务数据
*/
private T data;
public Item(long activeTime, T data) {
this.activeTime = activeTime * 1000 + System.currentTimeMillis();
this.data = data;
}
public long getActiveTime() {
return activeTime;
}
public T getData() {
return data;
}
/**
* 从这个方法返回到激活日期的剩余时间
*/
@Override
public long getDelay(TimeUnit unit) {
long time = unit.convert(this.activeTime - System.currentTimeMillis(), unit);
return time;
}
/**
* Delayed接口继承了Comparable接口,按剩余时间排序,实际计算考虑精度为纳秒数
*/
@Override
public int compareTo(Delayed o) {
long d = (getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
if (d == 0) {
return 0;
} else {
if (d < 0) {
return -1;
} else {
return 1;
}
}
}
}
/**
* @Author SunnyBear
* @Description 提交给框架执行的工作实体类,工作:表示本批次需要处理的同性质任务(Task)的一个集合
*/
public class JobInfo<R> {
/**
* 工作名,用以区分框架中唯一的工作
*/
private final String jobName;
/**
* 工作中任务的长度,即一个工作多少个任务
*/
private final int jobLength;
/**
* 处理工作中任务的处理器
*/
private final ITaskProcesser<?, ?> taskProcesser;
/**
* 任务的成功次数
*/
private AtomicInteger successCount;
/**
* 工作中任务目前已经处理的次数
*/
private AtomicInteger taskProcessCount