Koupleless 单进程多应用如何解决兼容问题

f10be1f1ccc30e6e52fa839380e90521.gif

文|苟振东(花名:盛知)

Koupleless 项目 committer

蚂蚁集团技术专家

本文 5789 字    阅读 12 分钟

本篇文章属于 Koupleless 进阶系列文章第二篇,默认读者对 Koupleless 的基础概念、能力都已经了解,如果还未了解过的可以查看官网https://koupleless.io/

进阶系列一可查看👉:Koupleless 模块化的优势与挑战,我们是如何应对挑战的

多应用兼容性

Koupleless 极速研发体系下存在的问题

Koupleless 是模块化研发的体系,应用被抽象成了基座模块两个部分,模块可以动态地安装到基座上,如下图所示:

6b9076ff8aadf5a4c604f2e31268ab0a.jpeg

通过该抽象,一个进程里可以运行多个应用,用户可以享受到:节省资源、快速部署、迭代提效等收益。具体解析可以参考文章:https://koupleless.io/docs/introduction/architecture/arch-principle/

这种单进程多应用的模式背后其实是共享与隔离的极致平衡。像上文中所说的,隔离可以带来独立的迭代升级能力,共享可以带来极致的启动速度与研发效率。但是共享也会带来互相干扰的问题,我们在进阶系列第一篇文章中提到,需要引入额外的兼容性治理。

首先兼容性问题大致可以分为 3 大类:

1、全局变量互相污染(多数为 static 变量、System Properties 导致)

2、ClassLoader 不匹配

3、部分资源不卸载(只有热部署时才会有)

综上,为了让用户能低成本地享受到 Koupleless 的收益,同时保证业务执行的正确性,我们设计了从问题的发现 -> 治理 -> 防御 角度全面治理此类问题的方案,在每个阶段分别提供了相应的工具和组件:

  问题发现

问题发现部分主要分为静态问题暴露和动态问题暴露两块:

  • 静态问题暴露:通过静态代码扫描工具,识别潜在的不兼容点,并由人工确认和修复。

  • 动态问题暴露:提供简单易用的 Koupleless 运行时集成测试框架,允许用户低成本地编写集成测试逻辑,回归验证模块行为符合预期。

  问题治理

当我们发现问题后,需要提供对应的兼容性修复方案。为此,我们提供了基座构建插件,自动进行兼容性修复,帮助用户低成本地解决兼容性问题。

  问题防御

集成测试框架,同样可以帮助完成治理后回归验证问题,避免版本升级带来的回归问题。

问题发现:代码扫描工具

扫描代码、定位问题、人工修复

在 Java 单进程多应用模式下,根据团队的经验积累,我们发现了一些常见的不兼容静态代码模式。基于这个现状,我们可以通过静态代码扫描工具来识别这些模式,在运行前暴露风险,让开发者尽早修复问题。

  常用的不兼容模式

常用的不兼容模式主要有 3 类:

  • 全局变量相互污染:比如基座通过 static 维护了一些全局变量,多个模块在写入 / 读取 static 变量的时候可能是用了同一个 key,进而导致潜在的互相污染的风险。

  • ClassLoader 不匹配问题:在 sofa-ark 类隔离机制中,属于模块自身的类只能由自己的 ClassLoader 加载。因此,在一些 ClassLoader 使用不正确的时候,例如用基座的 ClassLoader 加载模块类的全称,可能会出现预期外的问题。

  • 模块泄漏问题:在多模块架构中,模块是一个单独的运维单位,如果卸载模块时没有正确地关闭一些服务,例如 shutdown 线程池,可能会导致内存泄漏的问题。

  代码扫描工具

上述 3 类问题是 Koupleless 不兼容的主要问题,我们可以通过一些常见的代码片段模式进行问题识别和暴露这 3 类问题,然后由人工进行确认和修复。

为此,我们基于开源社区的 sonarqube 静态代码扫描服务,开发了针对性的静态代码扫描插件,以帮助开发者快速地发现问题,从而进行有效的治理。目前项目已经开源,地址在 https://github.com/koupleless/scanner

目前已有的扫描规则有:

  • static 变量扫描:扫描和暴露可修改的 static 变量(不可修改的变量无污染问题)。当然,由于工程中使用 static 变量是一种常见的模式,全部告警可能会造成大量噪音,因此我们也基于一定的特征(命名、类型等)进行了没有潜在风险的降噪处理。

  • class.forName 方法调用扫描:class.forName 会使用堆栈中 caller 的 ClassLoader 进行类加载,这往往是基座的 ClassLoader, 因此有较高的风险导致 ClassNotFound。

  • SomeClass.getClassLoader 方法:如果要加载的目标类和 SomeClass 不在一个模块中,则会导致 ClassNotFound 异常,有比较高的风险。

当前已经有一些企业在使用该工具。未来,扫描规则还会持续完善,也欢迎开源社区的各位在发现了新的不兼容模式后,将其完善成为规则,并且 PR 贡献,让静态代码扫描规则越来越完善!

问题治理:基座构建插件

让基座低成本地快速增强多应用模式

前一小节我们提到,Koupleless 运行时可能由于引入多应用污染的问题而导致需要兼容性修复,而兼容性修复又主要解决 3 大问题:

  • 修复原有代码:一些组件的某个版本已经固化,已不再允许提高代码修改,如何才能低成本的方式修改原有逻辑增强多应用的能力?

  • 多版本适配:同一组件不同版本之间的实现可能不同,导致修复方式也不同。如何修改这么多的版本,并做到长期的可维护呢?

  • 用户如何低成本地使用:每个组件对应不同版本可能有不同的增强逻辑,用户怎么知道具体要引入哪段增强逻辑呢?怎样才能让用户低成本甚至不感知的情况下,自动帮助找到对应的增强逻辑呢?

接下来,我们继续介绍一下是如何解决这些问题的。

  如何低成本增强组件原有代码

这里的低成本要考虑两点:

  • 组件本身代码增强的低成本,让 Koupleless 贡献者能低成本扩展一些组件支持多应用能力。

  • 每个组件存在许多历史版本,一个组件的不同版本其实现可能不同,进而需要增强的逻辑也不同,如何能低成本的增强这些历史版本,而非逐个版本的增强。

常见的手段有三种:同名类覆盖、反射、提交到修复的主分支。

修复方法

优点

缺点

✓同名类

覆盖

修复逻辑比较直观,还可以用 diff 软件和源文件进行实现的对比

需要用户引入额外的依赖,以及必须优先于原有实现被 JVM 加载

反射

不需要用户引入额外的依赖,可以由框架自动代理类和生成增强

无法直观地看到增强逻辑,可维护性比较差,可能有性能影响

提交到修复的主分支

用户只需要升级 SDK 即可修复

迭代周期比较长,无法及时解决用户问题,以及许多用户用的是历史老版本,官方可能不再维护接受 PR

为了有更好的可维护性,Koupleless 最终采用了同名类覆盖的办法,并将有关增强类统一放置在了独立仓库: https://github.com/koupleless/adapter

每个组件的多版本问题,所以这里也维护了组件不同版本的不同 adapter 实现列表,如 log4j2,这里 koupleless-adapter-log4j2-spring-starter-2.1 实际上是增强了springboot 2.1 版本到 3.2 的所有版本。

a7ad0e2afcb1037cadd134a0beb8ad88.png

  如何解决用户低成本使用问题

某个组件不同版本对应的增强逻辑不同,这也给用户带来了使用负担,为了进一步降低用户的接入成本,免去用户对照依赖版本查询增强的繁琐,我们也提供了 koupleless-base-build-plugin 插件,用户可以将如下构建插件添加到自己的 maven 工程中:

<plugin>
    <groupId>com.alipay.sofa.koupleless</groupId>
    <artifactId>koupleless-base-build-plugin</artifactId>
    <version>${koupleless.runtime.version}</version>
    <executions>
        <execution>
            <goals>
               <goal>add-patch</goal>
            </goals>
        </execution>
    </executions>
</plugin>

该插件会动态地解析用户使用的依赖,识别到需要增强的依赖,并动态地添加增强类,工作流程如下图所示:

bcc68a3a5ccd5a140e8e84f48b75e878.jpeg

值得注意的是,在使用增强的过程中,我们必须保证对于一个同名类,koupleless 维护的增强优先于原本的类被 classLoader 加载。

我们在 koupleless-base-build-plugin 中保证了这个优先级,保证的方法是在 maven 的 generated-sources 阶段将增强类拷贝到当前工程中,正如上述流程图的 5~6 步所示,而当前工程中的类加载优先级是最高的。用户可以参考 samples 工程 https://github.com/koupleless/samples/tree/main/springboot-samples/logging/log4j2 进行验证和实践,在当前项目下执行 maven clean package 命令后,可以看到构建结果如下图:

cfe8eaef04fd8ade27da440cfebee5fc.png

org.apache.logging 和 org.springframework.boot 是 koupleless 增强过的同名类,被自动拷贝到了当前项目中。

总结一下,用户可以通过在 maven 工程中引入 koupleless-base-build-plugin 插件保证其业务逻辑在多应用模式下的兼容性,其优势有:

  • 通过同名类覆盖的方式自动修复潜在的兼容性问题。

  • 通过动态依赖映射减少用户自己查询依赖到补丁的映射。

通过将补丁拷贝到当前的工程目录自动解决补丁优先级应该是最高的问题。

如此,用户只需要引入一个构建插件,即可快速地接入 Koupleless。

问题防御:集成测试框架

简单又快速验证正确性的效率神器

当然,由于这套模式在一个 JVM 里运行多个应用,不可避免地存在着一些兼容性问题,我们当然不能指望用户每次都到生产才发现这个问题。我们需要将风险左移,在本地测试验证的时候尽可能地暴露问题。不过,由于 Koupleless 是以动态加载 jar 包的模式实现单进程多应用的,我们也推出了集成测试框架 koupleless-test-suite 来尽可能地简化中间件、sdk 开发者的验证流程,也便于未来治理后的回归性验证。

  原生的集成测试编写方式有什么问题

那么 koupleless-test-suite 解决了什么问题呢?假设没有这个框架,当用户需要在本地验证代码的正确性时,其需要经历如下的操作步骤:

1、构建基座代码。

2、启动基座代码。

3、构建模块代码。

4、安装模块代码。

5、进行 http / rpc 调用验证接口结果。

如果步骤 5 失败,则用户需要反复地在 3~5 之间来回操作,并且会涉及在多个 IDE / 终端之间的来回切换。

上述步骤是原生的 Koupleless 模式无法避免的,因为 Koupleless 是多 ClassLoader 加载多个 jar 包的模式,在该模式下模块单独打包构建是必要的,但这又会引入比较繁琐的验证成本。

  集成测试框架如何解决了该问题

为了优化该问题,给用户提供简单直接的编程体验,即在 IDEA 里点一下 Debug 按钮即可调试了。我们需要一定的 mock 能力去在 1 个 jar 包中模拟出多个 jar 包的加载行为,使用户免于在多个项目之间来回切换。

最终呈现给用户的接口是非常简洁的,用户需要引入如下依赖:

<dependency>
  <groupId>com.alipay.sofa.koupleless</groupId>
  <artifactId>koupleless-test-suite</artifactId>
  <version>${koupleless.runtime.version}</version>
  <scope>test</scope>
</dependency>

启动基座 + 模块的样例代码如下:

public static void setUpMultiApplication() {
        multiApp = new TestMultiSpringApplication(MultiSpringTestConfig
            .builder()
            .baseConfig(BaseSpringTestConfig.builder()
                        .mainClass(BaseApplication.class)
                        .build()
                       )
            .bizConfigs(
                Lists.newArrayList(
                    BizSpringTestConfig.builder()
                        .bizName("biz1")
                        .mainClass(Biz1Application.class)
                        .build(),
                    BizSpringTestConfig.builder()
                        .bizName("biz2")
                         .mainClass(Biz2Application.class)
                        .build())
            ).build()
        );
        multiApp.run();
    }

上述代码会在一个进程中同时启动基座 + 模块 APP,并且底层类加载的行为和生产基本保持一致。

接着,我们就可以便捷地写验证逻辑了, 比如直接拿到模块内部的 Bean 并且进行行为的验证,如下:

Assert.assertEquals(
    "biz1",
    SpringServiceFinder.getModuleService(
        "biz1",
        null,
        StrategyService.class
    ).getAppName()
);

目前,Koupleless 自身的 samples 用例,也都通过这套测试框架来做功能性的测试验证,完整的测试用例可以参照工程样例:

https://github.com/koupleless/samples/tree/main/springboot-samples/web/tomcat/tomcat-web-integration-test

如果你对测试框架的实现方式感兴趣,欢迎参照官方文档 https://koupleless.io/docs/tutorials/multi_app_integration_test 对测试框架各个重要方法的简单介绍。

展望与规划

Koupleless 的长期主义

当然,为了能更好地让用户平滑地接入 Koupleless 模式,我们希望与社区共同完善配套工具链,不断完善静态代码扫描与动态集成测试规则,沉淀治理的 adapter,让更多的用户能更低成本的接入使用。

最后欢迎大家来使用 koupleless,并献上您宝贵的意见!

加入我们

蚂蚁集团中间件和 PaaS 产品技术火热招聘中!

阅读原文或扫描下方二维码,跳转招聘详情页~

d7113c9717e895bb83443376e83735f7.png

Koupleless Star 一下✨:

https://github.com/koupleless/koupleless

Koupleless官网:

https://koupleless.io/

  本周推荐阅读  

c51de4d59a9a0c2bbafdaaad41307e25.jpeg

Hi 同学,这里有两个 Koupleless 专属社区任务等你认领!

cea5134b4097252a863c0747ba9ba1be.png

Koupleless 内核系列|模块化隔离与共享带来的收益与挑战

b2b1f551201a67e33101d9f82af1c757.png

深度案例解读 Koupleless 在南京爱福路的落地实践

c783901c5a0a69c6042f89502fd9cd82.png

Koupleless 带来拆分插件,帮你提高协作开发效率!

22c8911845b439ef5ef826d9697e96fb.png

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【VTKExamples::Texture】第六期 TextureThreshold

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享VTK样例TextureThreshold,并解析接口vtkTexture,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~Y…

ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

网络空间资产梳理

网络空间资产梳理 网络安全建设的实质是对风险的管理&#xff0c;古人云&#xff1a;知己知彼百战不殆。所谓知己&#xff0c;就是要了解自己的资产以及这些资产的脆弱性&#xff0c;知彼就是了解外部威胁及威胁所使用的手段。要做到知己&#xff0c;首先就要对自身的资产进行梳…

虚拟局域网VLAN

前面的是ip地址&#xff0c;后面的子网掩码 hr和财务是俩网段&#xff0c;hr部门发的广播包财务部门也能收到&#xff0c;那么怎么把不同的网段隔离在不同的广播域里呢 如果只有俩部门就用路由器隔离就行了&#xff0c;但是部门一多每一个都用交换机就浪费钱了 可以通过虚拟…

在MySQL数据库中的视图和事务。

视图 view 临时表 作用&#xff1a;优化多表查询的效率 可以将经常使用的连接查询结果使用视图进行保存&#xff0c;避免多次重复的笛卡尔积运算 MySQL数据库在多表查询的时候会自动进行笛卡尔积运算。 如果将来经常要用到某一个多表查询的结果就可以使用视图将这个结果…

代码随想录——路径总和(Leetcode112)需要回顾

题目链接 递归 递归函数什么时候需要返回值&#xff1f; 如果需要搜索整棵二叉树且不需要处理递归返回值&#xff0c;递归函数不要返回值如果需要搜索整棵二叉树且需要处理递归返回值&#xff0c;递归函数需要返回值如果搜索其中一条条件的路径&#xff0c;递归一定需要返回值…

iOS App上架全流程及审核避坑指南

App Store作为苹果官方的应用商店&#xff0c;审核严格周期长一直让用户头疼不已&#xff0c;很多app都“死”在了审核这一关&#xff0c;那我们就要放弃iOS用户了吗&#xff1f;当然不是&#xff01;本期我们从iOS app上架流程开始梳理&#xff0c;详细了解下iOS app上架的那些…

升级版网创教程wordpress插件自动采集并发布

主要功能&#xff1a; wordpress 插件主题系列支持自动采集并发布。 主要采集: 福缘&#xff0c;中创&#xff0c;冒泡 自动采集各大项目网进行整合发布到自己个人网站 插件话更新&#xff0c;减少网络请求&#xff0c;提升稳定性 代码完美开源 傻瓜式操作&#xff0c;一…

计算机SCI期刊,中科院2区,收稿范围非常广泛!

一、期刊名称 Journal of Web Semantics 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;计算机科学 影响因子&#xff1a;2.5 中科院分区&#xff1a;2区 出版方式&#xff1a;开放出版 版面费&#xff1a;$1600 三、期刊征稿范围 《网络语义学杂志》…

Redis实现热点数据排行榜或游戏积分排行榜

数据库中的某张表中存储着文章的浏览量&#xff0c;或者点赞数等&#xff0c;或者游戏积分等数据...... 这些数据的更新在redis中完成&#xff0c;并定时同步到mysql数据库中。 而如果要对这些数据进行排序的话&#xff1a; Redis中的Sorted Set(有序集合)非常适合用于实现排…

ZEDmini使用完全指南

ZEDmini使用 ZED stereolabs 开箱测评 使用说明 ubuntu18.04nvidiacuda10 ubuntu18.04ZED SDK安装和使用 Ubuntu16.04安装NVIDIA显卡驱动 查看显卡信息 redwallredwall-G3-3500:~/catkin_ws$ lspci | grep VGA 00:02.0 VGA compatible controller: Intel Corporation Device …

成功案例(IF=7.4)| 代谢组+16s联合分析助力房颤代谢重构的潜在机制研究

研究背景 心房颤动&#xff08;AF&#xff09;是临床上最常见的持续性心律失常&#xff0c;具有显著的发病率和死亡率。高龄是房颤发病率、患病率和进展最显著的危险因素。与年龄在50-59岁之间的参与者相比&#xff0c;80-89岁之间的参与者患房颤的风险增加了9.33倍。目前尚不…

IEEE Transactions on Neural Networks and Learning Systems神经网络和学习系统TNNLS论文投稿须知

一、TNNLS介绍 IEEE Transactions on Neural Networks and Learning Systems作为控制领域的TOP期刊&#xff0c;2024年5月影响因子为10.4&#xff0c;虽然有些下降&#xff0c;之前五年平均影响因子为11.2&#xff0c;但依然是该领域王牌期刊&#xff0c;接收关于神经网络和相…

【软考中级 软件设计师】计算机网络和安全

计算机网络和安全是软件设计师&#xff08;软考中级&#xff09;考试中的重要组成部分&#xff0c;它涵盖了网络基础、网络协议、网络架构、网络安全等多个方面。以下是一些核心概念和要点&#xff0c; 计算机网络基础 OSI七层模型&#xff1a;物理层、数据链路层、网络层、传…

《intel开发手册卷3》读书笔记2

IA-32架构的内存管理分为两个部分&#xff1a;分段和分页。分段提供了一种隔离每个进程 或者任务代码、数据和栈模块的机制,保证多个进程或者任务能够在同一个处理器上运 行而不会互相干扰。分页机制实现了传统请求调页的虚拟内存系统&#xff0c;在这种系统中&#xff0c; 程序…

由于删除、修改、重装QT库引起的软件问题@FreeBSD

由于由于删除、修改、重装QT库以及snappy库等&#xff0c;导致很多软件出现了异常&#xff0c;即无法启动&#xff0c;逐个解决问题。 qutebrowser浏览器 报错&#xff1a; qutebrowser报错 No backend library found qutebrowser needs QtWebKit or QtWebEngine, but neith…

自动化测试用例结构

标准的用例结构&#xff1a; 用力标题前提条件用例步骤预期结果实际结果 测试用例对比&#xff1a;

好的架构是进化来的,不是设计来的

很多年前&#xff0c;读了子柳老师的《淘宝技术这十年》。这本书成为了我的架构启蒙书&#xff0c;书中的一句话像种子一样深埋在我的脑海里&#xff1a;“好的架构是进化来的&#xff0c;不是设计来的”。 2015 年&#xff0c;我加入神州专车订单研发团队&#xff0c;亲历了专…

【安装配置】WSL虚拟机导出、导入镜像(涉及到docker无法在wsl下使用的问题)

背景 WSL&#xff08;Windows Subsystem Linux&#xff09;&#xff0c;是微软提供的在Windows下便携地使用Linux系统的方式&#xff0c;它支持使用虚拟化技术&#xff08;也就是要在bios和控制面板中开启虚拟化支持&#xff09;&#xff0c;完美支持Ubuntu和Windows文件系统之…

分布式文件系统minIo

分布式文件系统 什么是分布式文件系统 一个计算机无法存储海量的文件&#xff0c;通过网络将若干计算机组织起来共同去存储海量的文件&#xff0c;去接收海量用户的请求&#xff0c;这些组织起来的计算机通过网络进行通信&#xff0c;如下图&#xff1a; 好处&#xff1a; 1、…