180723-Quick-Task 动态脚本支持框架之结构设计篇
文章链接:https://liuyueyi.github.io/hexblog/2018/07/23/180723-Quick-Task-动态脚本支持框架之结构设计篇/
Quick-Task 动态脚本支持框架之结构设计篇
相关博文:
前面两篇博文,主要是整体介绍和如何使用;接下来开始进入正题,逐步剖析,这个项目是怎么一步一步搭建起来的;本篇博文则主要介绍基本骨架的设计,围绕项目的核心点,实现一个基础的原型系统
I. 结构分析
整体设计图如下:
对于上面的图,得有一个基本的认知,最好是能在脑海中构想出整个框架运行的方式,在正式开始之前,先简单的过一下这张结构图
抓要点
1. 任务执行单元
即图中的每个task就表示一个基本的任务,有下面几个要求
- 统一的继承关系(面向对象的设计理念,执行同一个角色的类由某个抽象的接口继承而来)
- 任务的执行之间是没有关系的(即任务在独立的线程中调度执行)
2. 任务队列
在图中表现很明显了,在内存中会保存一个当前所有执行的任务队列(或者其他的容器)
这个的目的是什么?
- 任务脚本更新时,需要卸载旧的任务(因此可以从队列中找到旧的任务,并停掉)
- 任务脚本删除时,需要卸载旧的任务
3. 任务管理者
虽然图中并没有明确的说有这么个东西,但也好理解,我们的系统设计目标就是支持多任务的执行和热加载,那么肯定有个任务管理的角色,来处理这些事情
其要做的事情就一个任务热加载
- 包括动态脚本更新,删除,新增的事件监听
- 实现卸载内存中旧的任务并加载执行新的任务
4. 插件系统
这个与核心功能关系不大,可以先不care,简单说一下就是为task提供更好的使用的公共类
这里不详细展开,后面再说
II. 设计实现
有了上面的简单认知之后,开始进入正题,编码环节,省略掉创建工程等步骤,第一步就是设计Task的API
1. ITask设计
抽象公共的任务接口,从任务的标识区分,和业务调度执行,很容易写出下面的实现
public interface ITask {
/**
* 默认将task的类名作为唯一标识
*
* @return
*/
default String name() {
return this.getClass().getName();
}
/**
* 开始执行任务
*/
void run();
/**
* 任务中断
*/
default void interrupt() {}
}
前面两个好理解,中断这个接口的目的何在?主要是出于任务结束时的收尾操作,特别是在使用到流等操作时,有这么个回调就比较好了
2. TaskDecorate
任务装饰类,为什么有这么个东西?出于什么考虑的?
从上面可以知道,所有的任务最终都是在独立的线程中调度执行,那么我们自己实现的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任务时,进行硬编码风格的封装调用,完全没有问题(只是代码将不太好看而已)
3. TaskContainer
上面两个是具体的任务相关定义接口,接下来就是维护这些任务的容器了,最简单的就是用一个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
- 维护name的映射,主要是基于任务的唯一标识出发的,后续可能借此做一些扩展(比如任务和任务之间的关联等)
4. 任务注册
前面介绍了任务的定义和装载任务的容器,接下来可以想到的就是如何发现任务并注册了,这一块这里不要详细展开,后面另起一篇详解;主要说一下思路
在设计之初,就决定任务采用Groovy脚本来实现热加载,所以有两个很容易想到的功能点
- 监听Groovy脚本的变动(新增,更新,删除),对应的类为
TaskChangeWatcher
- 加载Groovy脚本到内存,并执行,对应的类为
GroovyCompile
5. 执行流程
有了上面四个是否可以搭建一个原型框架呢?
答案是可以的,整个框架的运行过程
- 程序启动,注册Groovy脚本变动监听器
- 加载groovy脚本,注册到TaskContainer
- 将groovy脚本丢到线程池中调度执行
- 执行完毕后,清除和回收现场
6. 其他
当然其他一些辅助的工具类可有可无了,当然从使用的角度出发,有很多东西还是很有必要的,如
- 通用的日志输出组件(特别是日志输出,收集,检索,经典的ELK场景)
- 报警相关组件
- 监控相关
- redis缓存工具类
- dao工具类
- mq消费工具类
- http工具类
- 其他
III. 其他
0. 相关
博文:
项目:
1. 一灰灰Blog: https://liuyueyi.github.io/hexblog
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
2. 声明
尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
- 微博地址: 小灰灰Blog
- QQ: 一灰灰/3302797840
3. 扫描关注
180723-Quick-Task 动态脚本支持框架之结构设计篇的更多相关文章
- 180807-Quick-Task 动态脚本支持框架之Groovy脚本加载执行
Quick-Task 动态脚本支持框架之Groovy脚本加载执行 上一篇简答说了如何判断有任务动态添加.删除或更新,归于一点就是监听文件的变化,判断目录下的Groovy文件是否有新增删除和改变,从而判 ...
- 180729-Quick-Task 动态脚本支持框架之任务动态加载
Quick-Task 动态脚本支持框架之任务动态加载 前面几篇博文分别介绍了整个项目的基本架构,使用说明,以及整体框架的设计与实现初稿,接下来则进入更细节的实现篇,将整个工程中核心实现捞出来,从为什么 ...
- 180719-Quick-Task 动态脚本支持框架之使用介绍篇
文章链接:https://liuyueyi.github.io/hexblog/2018/07/19/180719-Quick-Task-动态脚本支持框架之使用介绍篇/ Quick-Task 动态脚本 ...
- 【前端基础】动态脚本与JSONP
博主入职两个月了,越来越感受到打好基础对于前端工程师的重要性,在向着狂拽酷炫的框架&构建工具高速狂奔之前,必须有一个坚实的基础打底,才不至于轻易翻车.所以博主最近一直在恶补<JS高级程序 ...
- 携程Android App的插件化和动态加载框架
携程Android App的插件化和动态加载框架已上线半年,经历了初期的探索和持续的打磨优化,新框架和工程配置经受住了生产实践的考验.本文将详细介绍Android平台插件式开发和动态加载技术的原理和实 ...
- atitit.bsh BeanShell 的动态脚本使用java
atitit.bsh BeanShell 的动态脚本使用java 1.1. BeanShell是一个小巧免费的JAVA源码解释器 ,支持对象式的脚本语言特性,亦可嵌入到JAVA源代码中. 亦可嵌入到J ...
- JVM中的动态语言支持简介
抽丝剥茧 细说架构那些事——[优锐课] 从版本6开始,JVM已扩展为支持现代动态语言(也称为脚本语言).Java8的发行为这一领域提供了更多动力.感到这种支持的必要性是因为Java作为一种语言固有地是 ...
- AICompiler动态shape编译框架
AICompiler动态shape编译框架 移动互联网的兴起,不仅产生了海量数据,也对人机交互有了新的定义.企业如何动态处理不同规格图片数据,如何更灵活处理不同长度的对话语料等等,提升企业运营效率,争 ...
- 【开源】.Net 动态脚本引擎NScript
开源地址: https://git.oschina.net/chejiangyi/NScript 开源QQ群: .net 开源基础服务 238543768 .Net 动态脚本引擎 NScript ...
随机推荐
- mongoDB的常用语法
安装: 到mongodb官网下载安装包或者压缩包:https://www.mongodb.com/download-center?jmp=nav 1.如果是msi包的话则点击按步骤安装,如果是压缩包的 ...
- 使用Azcopy在Azure上进行HBase的冷热备份还原
场景 HBase表TaskLog中有20.55G数据(20553078551Byte),目前存放在热存储中,现在要移至冷热储,并进行还原. HBase目录:hbase/data/default 冷目录 ...
- Shell笔记-04
如果表达式中包含特殊字符,Shell 将会进行替换.例如,在双引号中使用变量就是一种替换,转义字符也是一种替换. 举个例子: #!/bin/bash a=10 echo -e "Value ...
- 大话Linux内核中锁机制之完成量、互斥量
大话Linux内核中锁机制之完成量.互斥量 在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内 ...
- Notes 20180310 : String第二讲_String的声明与创建
1 字符串的声明与创建 学习String的第一步就是创建(声明)字符串,我们在这里之所以分为创建和声明(其实是一个意思,都是创建字符串,但两者却有本质的区别)是因为String是一个很特殊的类,它的 ...
- Oracle数据库sql语句
1.创建用户.赋权限.删除用户 create user test identified by test default test users temporary tablespace temp; gr ...
- (Les16 执行数据库恢复)-控制文件恢复
测试丢失所有控制文件恢复[20180517] rman target / show all; configure channel 1 device type disk format ' ...
- Linux Centos平台下安装Nginx
以home下安装为例,切换到home目录下 cd /home 安装依赖 nginx相关依赖 yum -y install make gcc gcc-c++ openssl openssl-devel ...
- ionic 环境搭建
1.安装nodejs (8.4.0) 下载地址 https://nodejs.org/zh-cn/ 2.Java jdk 版本号 jdk1.8.0_121 3.安装 cordova npm ins ...
- 记一次MacBook Pro无法连接wifi网络修复
解决方案: https://blog.csdn.net/kimbing/article/details/79321001 真的哭了 原因是插入了我的USB3.0拓展坞影响了wifi信号 不知道原理是啥 ...