8.完成任务实现的SDK封装及插件式加载

news2024/11/26 15:25:31

1.设计

任务的实现目前完成了Modbus RTU、Modbus TCP、Virtule。任务实现应该是任意的,比如打印一段话,执行一句SQL等,所以系统内部的必然要做到可扩展。

要做到可扩展,首先第一步就是定义标准,所以我们首先需要封装任务实现的SDK(第一件事)。

还要考虑到,用户使用我们的框架,但是并不想修改我们框架内部的代码,而是自己建立仓库完成实现,动态加载到我们的系统中,所以我们还要提供插件式集成任务实现的能力(第二件事)。

所以下面就对这两个需求展开设计与实现。

1.1 封装任务实现的SDK

此任务较易实现,因为前面我们就对任务有了一定的封装,所以只需要将定义部分,摘抄出去形成独立的jar就可以了,说干就干

1.1.1 建立一个新的工程:dttask-protocol-sdk

dttask-protocol-sdk 就是一个简单的java jar

移动文件,注意更改import

1.1.2 建立一个新的工程:dttask-protocol-simulator

这是一个依赖dttask-protocol-sdk的任务实现工程

它没有什么特殊,和dttask-server里的Virtual里的实现基本一样,就是类名不一样。建这个类也是为了后续测试动态加载插件做准备

至此工程依赖结构如下图:

1.2 实现动态加载插件

上面已经完成了插件的编写,因为我们整体使用的spring,所以我们在实现插件时就需要完成spring的bean注册。

1.2.1 ProtocolController

提供一个flushProtocol的接口,里面完成对插件jar包的动态加载,原理就是:

  • 先使用classloader将jar包里的class都加载进来
  • 然后检测所有class有没有是spring的bean,是的话,就向spring容器注册它对应的BeanDefinition
  • 最后使用spring容器查找对应bean,完成bean的实例化以及任务的实现放进ProtocolManager中集中管理
@RestController
@Slf4j
public class ProtocolController {

    @Autowired
    private DefaultListableBeanFactory defaultListableBeanFactory;
    @Autowired
    private ProtocolManager protocolManager;

    @GetMapping("/flushProtocol")
    public void flushProtocol(@RequestParam(value = "jarAddress", required = false) String jarAddress) {
        if (jarAddress == null) {
            jarAddress = "D:\\workspaces\\dttask\\protocolJar\\dttask-protocol-simulator-1.0-SNAPSHOT.jar";
        }
        String jarPath = "file:/" + jarAddress;
        hotDeployWithSpring(jarAddress, jarPath);
    }

    /**
     * 加入jar包后 动态注册bean到spring容器,包括bean的依赖
     */
    public void hotDeployWithSpring(String jarAddress, String jarPath) {
        Set<String> classNameSet = readJarFile(jarAddress);
        try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader())) {
            for (String className : classNameSet) {
                Class<?> clazz = urlClassLoader.loadClass(className);
                if (isSpringBeanClass(clazz)) {
                    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
                    String beanName = transformName(className);
                    defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
                }
            }
        } catch (ClassNotFoundException e) {
            throw new BusinessException("hotDeployWithSpring ClassNotFoundException", e);
        } catch (MalformedURLException e) {
            throw new BusinessException("hotDeployWithSpring MalformedURLException", e);
        } catch (IOException e) {
            throw new BusinessException("hotDeployWithSpring IOException", e);
        } finally {
            protocolManager.refreshMap();
        }


    }

    /**
     * 读取jar包中所有类文件
     */
    public static Set<String> readJarFile(String jarAddress) {
        Set<String> classNameSet = new HashSet<>();
        Enumeration<JarEntry> entries;
        try (JarFile jarFile = new JarFile(jarAddress)) {
            //遍历整个jar文件
            entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry jarEntry = entries.nextElement();
                String name = jarEntry.getName();
                if (name.endsWith(".class")) {
                    String className = name.replace(".class", "").replace("/", ".");
                    classNameSet.add(className);
                }
            }
        } catch (IOException e) {
            log.error("readJarFile exception:", e);
            throw new BusinessException("readJarFile exception", e);
        }
        return classNameSet;
    }

    /**
     * 方法描述 判断class对象是否带有spring的注解
     */
    public static boolean isSpringBeanClass(Class<?> cla) {
        if (cla == null) {
            return false;
        }
        // 不是抽象类 接口 且 没有以下注解
        return (cla.getAnnotation(Component.class) != null
                || cla.getAnnotation(Repository.class) != null
                || cla.getAnnotation(Service.class) != null)
                && !Modifier.isAbstract(cla.getModifiers())
                && !cla.isInterface();
    }

    /**
     * 类名首字母小写 作为spring容器beanMap的key
     */
    public static String transformName(String className) {
        String tmpstr = className.substring(className.lastIndexOf(".") + 1);
        return tmpstr.substring(0, 1).toLowerCase() + tmpstr.substring(1);
    }


    /**
     * 删除jar包时 需要在spring容器删除注入
     */
    public void delete(String jarAddress, String jarPath) {
        Set<String> classNameSet = readJarFile(jarAddress);
        try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)},
                Thread.currentThread().getContextClassLoader())) {
            for (String className : classNameSet) {
                Class<?> clazz = urlClassLoader.loadClass(className);
                if (isSpringBeanClass(clazz)) {
                    defaultListableBeanFactory.removeBeanDefinition(transformName(className));
                }
            }
        } catch (MalformedURLException e) {
            throw new BusinessException("delete MalformedURLException", e);
        } catch (IOException | ClassNotFoundException e) {
            throw new BusinessException("delete IOException or ClassNotFoundException", e);
        } 
    }
}

1.2.2 JobController

完成对Job的启动和停止

@RestController
@Slf4j
public class JobController {
    
    @Autowired
    private NetworkService networkService;
    
    @GetMapping("/stopDttaskJob")
    public void stopJobId(@RequestParam("dttaskJobId") long dttaskJobId) {
        Set<Long> dttaskJobIds = new HashSet<>();
        dttaskJobIds.add(dttaskJobId);
        networkService.stopCollect(dttaskJobIds);
    }

    @GetMapping("/startDttaskJob")
    public void startJobId(@RequestParam("dttaskJobId") long dttaskJobId) {
        Set<Long> dttaskJobIds = new HashSet<>();
        dttaskJobIds.add(dttaskJobId);
        networkService.startCollect(dttaskJobIds);
    }
    
}

2. 测试

  • 使用ApiPost建立3个请求

  • 启动三个节点,三个节点完成选举,并各自执行2个任务

  • dttask-protocol-simulator工程打一个jar包,放到 D:\workspaces\dttask\protocolJar 目录下

  • 对1号controller节点,发送停止任务请求

  • 将停止的任务的link_type字段改为-2并保存(-2是simulator实现的任务类型)

  • 发送协议实现接口调用

  • dttask-protocol-simulator加上一个断点,方便后续的查看

  • 发送任务启动接口

进入断点,表示 外部的插件已经动态加载进来了

至此 封装SDK 和 动态加载插件完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1319270.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nodejs+vue+微信小程序+python+PHP基于大数据的银行信用卡用户的数仓系统的设计与实现-计算机毕业设计推荐

银行信用卡用户的数仓系统综合网络空间开发设计要求。目的是将银行信用卡用户的数仓系统从传统管理方式转换为在网上管理&#xff0c;完成银行信用卡用户的数仓管理的方便快捷、安全性高、交易规范做了保障&#xff0c;目标明确。银行信用卡用户的数仓系统可以将功能划分为管理…

从文字下乡到人人学英语

从建国到改革开放&#xff0c;从恢复高考到新式教育改革&#xff0c;中国飞速发展&#xff0c;文字需求也在不断增大&#xff0c;在“地球村”的时代下&#xff0c;我们要“习文字之变&#xff0c;顺时代发展。” 古言道&#xff1a;“仓颉作书&#xff0c;后稷作稼”&#xff…

【Spark面试】Spark面试题答案

目录 1、spark的有几种部署模式&#xff0c;每种模式特点&#xff1f;&#xff08;☆☆☆☆☆&#xff09; 2、Spark为什么比MapReduce块&#xff1f;&#xff08;☆☆☆☆☆&#xff09; 3、简单说一下hadoop和spark的shuffle相同和差异&#xff1f;&#xff08;☆☆☆☆☆…

Linux学习教程(第十三章 Linux数据备份与恢复)

第十三章 Linux数据备份与恢复 不知道大家有没有丢失过重要的数据呢&#xff1f; 丢失数据的理由是多种多样的&#xff0c;有人是因为重装系统时&#xff0c;没有把加密文件的密钥导出&#xff0c;重装系统后密钥丢失&#xff0c;导致所有的加密数据不能解密&#xff1b;也有人…

实验记录:深度学习模型收敛速度慢有哪些原因

深度学习模型收敛速度慢有哪些原因&#xff1f; 学习率设置不当&#xff1a; 学习率是算法中一个重要的超参数&#xff0c;它控制模型参数在每次迭代中的更新幅度。如果学习率过大&#xff0c;可能会导致模型在训练过程中的振荡&#xff0c;进而影响到收敛速度&#xff1b;如果…

字符设备驱动框架的编写

一. 简介 我们在学习裸机或者 STM32 的时候关于驱动的开发就是初始化相应的外设寄存器&#xff0c;在 Linux 驱动开发中&#xff0c;肯定也是要初始化相应的外设寄存器。 只是在 Linux 驱动开发中&#xff0c; 我们需要按照其规定的框架来编写驱动&#xff0c;所以说学 …

【网络安全】网络防护之旅 - Java安全机制探秘与数字证书引爆网络防线

&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《网络安全之道 | 数字征程》⏰墨香寄清辞&#xff1a;千里传信如电光&#xff0c;密码奥妙似仙方。 挑战黑暗剑拔弩张&#xff0c;网络战场誓守长。 目录 &#x1f608;1. 初识网络安…

机场信息集成系统系列介绍(3):机场运行核心数据库(AODB)

目录 1、背景&#xff1a;什么是AODB 2、AODB包括哪些内容 3、AODB还应该适配哪些场景 4、一点点拓展 机场运行核心数据库&#xff08;AODB&#xff09;Airport Operation DataBase 1、背景&#xff1a;什么是AODB 在机场繁重的航班保障和旅客服务背后&#xff0c;庞大的…

centos安装了curl却报 -bash: curl: command not found

前因 我服务器上想用curl下载docker-compress&#xff0c;发现没有curl命令&#xff0c;就去下载安装&#xff0c;安装完成之后&#xff0c;报-bash: curl: command not found 解决方法 [rootcentos ~]# rpm -e --nodeps curl warning: file /usr/bin/curl: remove failed: …

非常好用的C++跨平台网络通信Mongoose,随笔记录

简介 Mongoose 是一个 C/C 网络库。它实现了事件驱动&#xff0c; TCP、UDP、HTTP、WebSocket、MQTT 的非阻塞 API。它连接设备 并将它们带到网上。自 2004 年以来&#xff0c;一些开源和商业 产品已经利用了它。它甚至运行在 国际空间站&#xff01; Mongoose 使嵌入式网络编…

【数据结构】(堆)Top-k|堆排序

目录 概念&#xff1a; 堆的实现 构建 初始化 销毁 插入元素 往上调整 删除堆顶元素 往下调整 返回堆顶元素 返回有效个数 是否为空 堆排序 Top-k问题 ​编辑 创建数据 堆top-k 概念&#xff1a; 堆是将数据按照完全二叉树存储方式存储到一维数组中&#xff…

Python实验项目9 :网络爬虫与自动化

实验 1&#xff1a;爬取网页中的数据。 要求&#xff1a;使用 urllib 库和 requests 库分别爬取 http://www.sohu.com 首页的前 360 个字节的数据。 # 要求&#xff1a;使用 urllib 库和 requests 库分别爬取 http://www.sohu.com 首页的前 360 个字节的数据。 import urllib.r…

网络 / day02 作业

1. TCP和UDP通信模型 1.1 TCP server #include <myhead.h>#define PORT 9999 #define IP "192.168.250.100"int main(int argc, const char *argv[]) {//1. create socketint sfd -1;if( (sfd socket(AF_INET, SOCK_STREAM, 0 ))-1 ){perror("socke…

YOLOv5改进 | SPPF | 将RT-DETR模型AIFI模块和Conv模块结合替换SPPF(全网独家改进)

一、本文介绍 本文给大家带来是用最新的RT-DETR模型中的AIFI模块来替换YOLOv5中的SPPF。RT-DETR号称是打败YOLO的检测模型&#xff0c;其作为一种基于Transformer的检测方法&#xff0c;相较于传统的基于卷积的检测方法&#xff0c;提供了更为全面和深入的特征理解&#xff0c…

机器学习 | KNN算法

一、KNN算法核心思想和原理 1.1、怎么想出来的&#xff1f; 近朱者赤&#xff0c;近墨者黑&#xff01; 距离决定一切、民主集中制 1.2、基本原理 —— 分类 k个最近的邻居 民主集中制投票分类表决与加权分类表决 1.3、基本原理 —— 回归 计算未知点的值决策规则不同均值法与…

Vue2-动态组件案例

1.component介绍 说明&#xff1a; Type: string | ComponentDefinition | ComponentConstructor Explanation: String: 如果你传递一个字符串给 is&#xff0c;它会被视为组件的名称&#xff0c;用于动态地渲染不同类型的组件。这是一个在运行时动态切换组件类型的常见用例。…

西南科技大学数字电子技术实验六(智力竞赛抢答器的设计及FPGA实现)FPGA部分

一、实验目的 进一步掌握D触发器&#xff0c;分频电路&#xff0c;多谐振荡电器&#xff0c;CP时钟脉冲元等单元电路的设计。 二、实验原理 三、程序清单&#xff08;每条语句必须包括注释或在开发窗口注释后截图&#xff09; 逻辑代码&#xff1a; module contend ( inpu…

OpenCV 1.1:读取本地图片显示灰度图及CV版本

imread方法加载图像 imread 功能是加载图像文件成为一个 Mat 对象&#xff0c;其中第一个参数表示图像文件名称&#xff0c;第二个参数表示加载的图像是什么类型&#xff0c;支持常见的三个参数值IMREAD_UNCHANDED(<0) 表示加载原图&#xff0c;不做任何改变IMREAD_GRAYSCA…

Docker插件和扩展:深入Docker功能的完整指南

Docker作为一种流行的容器化技术&#xff0c;不仅令应用程序的部署更为便捷&#xff0c;同时也提供了丰富的插件和扩展机制&#xff0c;以满足更多复杂场景下的需求。本文将深入研究Docker的插件和扩展&#xff0c;提供更为详实和全面的示例代码&#xff0c;助力读者更好地理解…

DPO讲解

PPO算法的pipeline冗长&#xff0c;涉及模型多&#xff0c;资源消耗大&#xff0c;且训练极其不稳定。DPO是斯坦福团队基于PPO推导出的优化算法&#xff0c;去掉了RW训练和RL环节&#xff0c;只需要加载一个推理模型和一个训练模型&#xff0c;直接在偏好数据上进行训练即可&am…