文章连接:liuyueyi.github.io/hexblog/201…java
相关博文:git
前面两篇博文,主要是总体介绍和如何使用;接下来开始进入正题,逐步剖析,这个项目是怎么一步一步搭建起来的;本篇博文则主要介绍基本骨架的设计,围绕项目的核心点,实现一个基础的原型系统github
总体设计图以下:redis
对于上面的图,得有一个基本的认知,最好是能在脑海中构想出整个框架运行的方式,在正式开始以前,先简单的过一下这张结构图api
抓要点缓存
即图中的每一个task就表示一个基本的任务,有下面几个要求框架
在图中表现很明显了,在内存中会保存一个当前全部执行的任务队列(或者其余的容器)ide
这个的目的是什么?工具
虽然图中并无明确的说有这么个东西,但也好理解,咱们的系统设计目标就是支持多任务的执行和热加载,那么确定有个任务管理的角色,来处理这些事情学习
其要作的事情就一个任务热加载
这个与核心功能关系不大,能够先不care,简单说一下就是为task提供更好的使用的公共类
这里不详细展开,后面再说
有了上面的简单认知以后,开始进入正题,编码环节,省略掉建立工程等步骤,第一步就是设计Task的API
抽象公共的任务接口,从任务的标识区分,和业务调度执行,很容易写出下面的实现
public interface ITask {
/** * 默认将task的类名做为惟一标识 * * @return */
default String name() {
return this.getClass().getName();
}
/** * 开始执行任务 */
void run();
/** * 任务中断 */
default void interrupt() {}
}
复制代码
前面两个好理解,中断这个接口的目的何在?主要是出于任务结束时的收尾操做,特别是在使用到流等操做时,有这么个回调就比较好了
任务装饰类,为何有这么个东西?出于什么考虑的?
从上面能够知道,全部的任务最终都是在独立的线程中调度执行,那么咱们本身实现的Task确定都是会封装到线程中的,在Java中能够怎么起一个线程执行呢?
一个顺其天然的想法就是包装一下ITask接口,让它集成自Thread,而后就能够简单的直接将任务丢到线程池中便可
@Slf4j
public class ScriptTaskDecorate extends Thread {
private ITask task;
public ScriptTaskDecorate(ITask task) {
this.task = task;
setName(task.name());
}
@Override
public void run() {
try {
task.run();
} catch (Exception e) {
log.error("script task run error! task: {}", task.name());
}
}
@Override
public void interrupt() {
task.interrupt();
}
}
复制代码
说明:
上面这个并非必须的,你也彻底能够本身在线程池调度Task任务时,进行硬编码风格的封装调用,彻底没有问题(只是代码将不太好看而已)
上面两个是具体的任务相关定义接口,接下来就是维护这些任务的容器了,最简单的就是用一个Map来保存,uuid到task的映射关系,而后再须要卸载/更新任务时,停掉旧的,添加新的任务,对应的实现也比较简单
public class TaskContainer {
/** * key: com.git.hui.task.api.ITask#name() */
private static Map<String, ScriptTaskDecorate> taskCache = new ConcurrentHashMap<>();
/** * key: absolute script path * * for task to delete */
private static Map<String, ScriptTaskDecorate> pathCache = new ConcurrentHashMap<>();
public static void registerTask(String path, ScriptTaskDecorate task) {
ScriptTaskDecorate origin = taskCache.get(task.getName());
if (origin != null) {
origin.interrupt();
}
taskCache.put(task.getName(), task);
pathCache.put(path, task);
AsynTaskManager.addTask(task);
}
public static void removeTask(String path) {
ScriptTaskDecorate task = pathCache.get(path);
if (task != null) {
task.interrupt();
taskCache.remove(task.getName());
pathCache.remove(path);
}
}
}
复制代码
说明
为何有两个map,一个惟一标识name为key,一个是task的全路径为key?
pathCache
前面介绍了任务的定义和装载任务的容器,接下来能够想到的就是如何发现任务并注册了,这一块这里不要详细展开,后面另起一篇详解;主要说一下思路
在设计之初,就决定任务采用Groovy脚原本实现热加载,因此有两个很容易想到的功能点
TaskChangeWatcher
GroovyCompile
有了上面四个是否能够搭建一个原型框架呢?
答案是能够的,整个框架的运行过程
固然其余一些辅助的工具类无关紧要了,固然从使用的角度出发,有不少东西仍是颇有必要的,如
博文:
项目:
一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛
尽信书则不如,已上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激