180729-Quick-Task 动态脚本支持框架之任务动态加载

简介: 前面几篇博文分别介绍了整个项目的基本架构,使用说明,以及整体框架的设计与实现初稿,接下来则进入更细节的实现篇,将整个工程中核心实现捞出来,从为什么这么设计到最终的实现给予说明

前面几篇博文分别介绍了整个项目的基本架构,使用说明,以及整体框架的设计与实现初稿,接下来则进入更细节的实现篇,将整个工程中核心实现捞出来,从为什么这么设计到最终的实现给予说明


I. 任务动态加载



这个动态脚本调度框架,最大的一个功能点就是支持热加载了,何为热加载?

简单来说就是在程序不宕机的情况下,可以往里面添加新的任务,删除旧的任务,更新已有的任务等等,就好比飞机在天上飞的时候给它加油,就这么高端的操作(😊)

为了支持热加载,首先面临的问题就是如何判断有任务的新增/删除/修改?


1. 任务监听的从0到1


要实现任务变更的监听,自然而然想到的一个方案就是起一个线程,不断的轮询,基本的逻辑无非是判断是否有任务的变更发生而已,也因此关注点就落在了如何判断任务是否有变更了


最简单粗暴直观的方法,就是记录之前的所有的任务,然后每次轮询时判断当前的所有任务与之前的所有任务是否有区别


再落到具体的实现上,则与任务的具体存储有关系了。很容易想到了几种任务存储方式有


  • 文件
  • 数据库 (如mysql)
  • 缓存 (如redis)


a. 数据库存储方式


这种方法比较好想到,同时也好实现,所有的任务都直接存在DB的某张表中;只需要保证表中包含以下几个字段即可


  • task: 具体的任务脚本逻辑
  • state: 任务的状态(表示运行,暂停等)
  • update: 任务的更新时间


基于上面的三个属性,判断的逻辑就清晰了


  • 起一个check线程,不断的扫表,获取所有的任务
  • 将当前获取的任务与上一次获取的任务进行对比
  • 根据比对的结果,封装任务更新事件,抛给下游处理


当然在db的场景下,还有一个更简单的方式,借助mysql的dbevent事件来处理任务的变更


  • 首先开启mysql的dbevent事件,即db中记录的新增删除变更都会抛出一个消息
  • 监听dbevent,以此封装任务的更新事件,丢给下一层进行处理即可


说明:

从个人的角度出发,在实际的应用场景中,基于DB的存储方案是比较合适的,然而我并没有去做这一个,因为相比于文件的方式,有点重量级了; 而我自己的实际项目中,文件方式已经足够解决我的需求了


b. 文件存储方式


这个可以说是最先想到,也是最容易实现的一种方式了。我的所有任务都放在指定的目录下,然后监听这个目录下所有文件的变动即可


QuickTask项目中,默认的实现方式,就是基于文件存储的动态任务监听,好处是简单,实现简单,理解简单,用起来也简单


c. 缓存存储方式


无非是拿缓存做db存储的思路,也没有啥其他的讲究,当然我并没有想过真的去拿缓存来实现,感觉这种实现方式有点非主流,当然也没有什么明显的优势


2. 任务监听的实现


上面是基本上把我如何实现动态任务监听的想法都写出来了,接下来就是具体的实现了,采用本地文件来存储具体的任务脚本,那么任务变化监听,就转换为了目录下文件变动的监听了


到了这一步,具体的实现方案就出来了,要实现文件变动监听,jdk7就提供了WatchService,当然还有大名鼎鼎的 commons-io,两个都可以实现,下面贴出commons-io的实现方式


@Slf4j
public class TaskChangeWatcher {
    private static final String SCRIPT_TYPE = ".groovy";
    public static boolean registerWatcher(File file) {
        try {
            long period = TimeUnit.SECONDS.toMillis(1);
            // 使用commons-io的文件观察器,实现文件动态变动的监听
            FileAlterationObserver observer =
                    new FileAlterationObserver(file, FileFilterUtils.and(FileFilterUtils.fileFileFilter()));
            observer.addListener(new TaskChangeListener());
            FileAlterationMonitor monitor = new FileAlterationMonitor(period, observer);
            monitor.start();
            return true;
        } catch (Exception e) {
            log.error("register watcher for script task change error! file: {} e:{}", file.getAbsolutePath(), e);
            return false;
        }
    }
    static final class TaskChangeListener extends FileAlterationListenerAdaptor {
        private boolean ignore(File file) {
            return !file.getName().endsWith(SCRIPT_TYPE);
        }
        private void addTask(File file) {
            ITask script = ScriptLoadUtil.loadScript(file);
            if (script == null) {
                return;
            }
            // 更新context中缓存,并启动任务
            ScriptTaskDecorate task = new ScriptTaskDecorate(script);
            TaskContainer.registerTask(file.getAbsolutePath(), task);
        }
        @Override
        public void onFileCreate(File file) {
            if (ignore(file)) {
                return;
            }
            addTask(file);
            // 在线程池中执行task
            log.info("add task : {}", file.getAbsolutePath());
        }
        @Override
        public void onFileChange(File file) {
            if (ignore(file)) {
                return;
            }
            addTask(file);
            // 在线程池中执行task
            log.info("task changed : {}", file.getName());
        }
        @Override
        public void onFileDelete(File file) {
            if (ignore(file)) {
                return;
            }
            // 文件删除,表示需要卸载旧的task
            TaskContainer.removeTask(file.getAbsolutePath());
            log.info("task delete: {}", file.getName());
        }
    }
}
复制代码


上面的实现,核心就是注册目录变动的监听,当出现文件的变化时,判断是否为groovy脚本,然后加载任务,并丢给任务容器进行调度执行


对于文件变动的监听的具体方案和讲解,如有疑问可以参考我之前的一篇博文:

  • Java可以如何实现文件变动的监听



相关文章
|
3月前
|
前端开发 开发者
在前端开发中,webpack 作为模块打包工具,其 DefinePlugin 插件可在编译时动态定义全局变量,支持环境变量定义、配置参数动态化及条件编译等功能。
在前端开发中,webpack 作为模块打包工具,其 DefinePlugin 插件可在编译时动态定义全局变量,支持环境变量定义、配置参数动态化及条件编译等功能。本文阐述 DefinePlugin 的原理、用法及案例,包括安装配置、具体示例(如动态加载资源、配置接口地址)和注意事项,帮助开发者更好地利用此插件优化项目。
91 0
webpack优化篇(四十三):多进程/多实例构建:资源并行解析可选方案
webpack优化篇(四十三):多进程/多实例构建:资源并行解析可选方案
153 0
webpack优化篇(四十三):多进程/多实例构建:资源并行解析可选方案
|
开发者
webpack进阶篇(二十八):优化构建时命令行的显示日志
webpack进阶篇(二十八):优化构建时命令行的显示日志
550 0
webpack进阶篇(二十八):优化构建时命令行的显示日志
|
缓存 监控 NoSQL
Quick-Task 动态脚本支持框架之结构设计篇
前面两篇博文,主要是整体介绍和如何使用;接下来开始进入正题,逐步剖析,这个项目是怎么一步一步搭建起来的;本篇博文则主要介绍基本骨架的设计,围绕项目的核心点,实现一个基础的原型系统
281 0
Quick-Task 动态脚本支持框架之结构设计篇
|
存储 JavaScript 前端开发
企业级项目开发中的交互式解释器以及global全局定义、Stream流的合理运用和实战【Note.js】
企业级项目开发中的交互式解释器以及global全局定义、Stream流的合理运用和实战【Note.js】
|
机器学习/深度学习 SQL 人工智能
PAI Designer Python脚本V2组件使用异常临时解决方案
PAI Designer(Studio 2.0)是基于云原生架构Pipeline Service(PAIFlow)的可视化建模工具, 提供可视化的机器学习开发环境,实现低门槛开发人工智能服务。同时,系统提供丰富且成熟的机器学习算法,覆盖商品推荐、金融风控及广告预测等场景,可以满足您不同方向的业务需求。PAI-Designer提供了自定义Python脚本的功能,您可以使用该组件运行自定义的Python函数,并且支持自定义安装依赖包。但是由于版本的更新Python脚本V2组件目前还有一些bug需要修复。本文为您演示使用Python脚本V2组件常见的一个异常及临时的处理方案,以供参考。
611 0
PAI Designer Python脚本V2组件使用异常临时解决方案
|
测试技术 PHP
Laravel 8 新特性: 动态Blade组件、事件监听器优化、事件测试助手
Laravel 8 通过引入 Laravel Jetstream,模型工厂类,迁移压缩,队列批处理,改善速率限制,队列改进,动态 Blade 组件,Tailwind 分页视图, 时间测试助手,artisan serve 的改进,事件监听器的改进,以及各种其他错误修复和可用性改进,对 Laravel 7.x 继续进行了改善。
317 0
VS Code项目中共享自定义的代码片段方案
VS Code项目中共享自定义的代码片段方案
|
缓存 JSON NoSQL
180628-动态任务执行框架想法篇
对于后端而言,数据订正可算是非常非常频繁且常见的事情了,常见的有DB、缓存、内存等数据源中的数据订正,对于非应用内存而言,其他有实体或者可以直接通过官方的提供的控制台连接进行修改的数据订正,相对比较简单,而对于应用内存,如果没有应用内通知并处理相关逻辑,多半就只能重启应用来实现刷新内存缓存了
166 0
180628-动态任务执行框架想法篇
|
Java 调度 开发工具
QuickTask动态脚本支持框架整体介绍篇
一个简单的动态脚本调度框架,支持运行时,实时增加,删除和修改动态脚本,可用于后端的进行接口验证、数据订正,执行定时任务或校验脚本
263 0
QuickTask动态脚本支持框架整体介绍篇