Jar包冲突解决方案调研

工做中特别是中间件落地时经常会遇到jar包冲突的状况,本篇文章将分析目前业界的Jar包隔离解决方案。java

一.jar包冲突的本质

Java 应用程序因某种因素,加载不到正确的类而致使其行为跟预期不一致。bash

二. jar包冲突的两种状况

第一类jar包冲突问题(同一jar包版本不一样)

  1. 应用程序依赖的同一个 Jar 包出现了多个不一样版本,并选择了错误的版本而致使 JVM 加载不到须要的类或加载了错误版本的类。
  2. 出现该问题的三个必要条件:
  • 依赖树中出现了同一个jar包的多个版本。
  • 该jar包的多个版本之间接口发生了变化(类名,方法签名变化,方法行为变化)
  • maven 的仲裁机制选择了错误的版本

第二类jar包冲突问题(不一样jar包的同一类版本不一样)

  1. 一样的类(类的全限定名彻底同样)出如今多个不一样的依赖 Jar 包中,即该类有多个版本,并因为 Jar 包加载的前后顺序(Maven的路径最短和覆盖优先原则)致使 JVM 加载了错误版本的类。如:假设有 ABC 三个jar包,因为 Jar 包依赖的路径长短、声明的前后顺序或文件系统的文件加载顺序等缘由,类加载器首先从 Jar 包 A 中加载了该类后,就不会加载其他 Jar 包中的这个类了。
  2. 出现该问题的三个必要条件:
  • 同一类M出如今了两个(或两个以上)不一样的jar包A、B中。
  • 类M在A、B中有差别,行为不一样。
  • 加载的类M不是咱们想要的。

三.解决方案

方法一:手动排查框架

  1. 根据异常堆栈信息肯定致使冲突的类名。
  2. 经过mvn dependency:tree -Dverbose -Dincludes=<groupId>:<artifactId> 查看是哪一个地方引入的jar包的版本。
  3. 若是是第一类 Jar 包冲突,则可用< excludes>排除不须要的 Jar 包版本或者在依赖管理<dependencyManagement>申明版本。
  4. 若是是第二类Jar包冲突,若是能够排除,则用 <excludes> 排掉不须要的那个 Jar 包,若不能排,则需考虑 Jar 包的升级或换个别的 Jar 包。

方法二:经过自定义ClassLoader实现隔离 在博客 Java 中隔离容器的实现 中提到了将每一个jar包视为多个bundle,经过自定义classloader隔离运行,而且还能够实现多个jar包共享一个类。 该Demo经过启动一个KContainer类运行,KContainer类中主要包含一个BundleList和SharedClassList。每个Bundle表明一个jar包或者class路径,Bundle类包含一个自定义的BundleClassLoader类(继承UrlClassLoader),因为不一样BundleBundleClassLoader不一样,能够实现隔离运行,而这个BundleClassLoader须要传入一个SharedClassList,classloader在加载一个类时,若是没有加载到,则能够从外部传进来的SharedClassList中加载,这样就实现了多个jar包共享一个类运维

protected Class<?> findClass(String name) throws ClassNotFoundException {
   logger.debug(“try find class {}”, name);
   Class<?> claz = null;
   try {
     claz = super.findClass(name);
  } catch (ClassNotFoundException e) {
     claz = null;
  }
   if (claz != null) {
     logger.debug(“load from class path for {}”, name);
     return claz;
  }
   //若是没有加载到,从共享的类中加载
   claz = sharedClasses.get(name);
   if (claz != null) {
     logger.debug(“load from shared class for {}”, name);
     return claz;
  }
   logger.warn(“not found class {}”, name);
   throw new ClassNotFoundException(name);
}
复制代码

须要共享出去给别人用的类能够经过在类路径下经过一个properties文件指定,在loadBundle的时候加载进SharedClassList。maven

方法三:轻量级隔离容器SOFAArk SOFAArk一样也是使用不一样的类加载器加载冲突的三方依赖包,进而作到在同一个应用运行时共存。 SOFAArk经过Ark Plugin区分应用中哪些依赖包是须要单独的类加载器加载。借助 SOFABoot 官方提供的 maven 打包插件,开发者能够把若干普通的 JAR 包打包成 Ark Plugin 供应用依赖或者把普通的 Java 模块改形成 Ark Plugin。应用使用添加 maven 依赖的方式引入 Ark Plugin,运行时,SOFAArk 框架会自动识别应用的三方依赖包中是否含有 Ark Plugin,进而使用单独的类加载器加载。其运行时逻辑图以下:源码分析

image-20180807120802894

  1. SOFAArk 容器处于最底层,负责启动应用。
  2. 每一个 Ark Plugin 都由 SOFAArk 容器使用独立的类加载器加载,相互隔离。
  3. 应用业务代码及其余非 Ark Plugin 的普通三方依赖包,统称为 Ark Biz。须要依赖下层的Ark Plugin。

在Ark Plugin的POM文件中,会配置导出类和导入类的配置。导出类即把 Ark Plugin 中的类导出给 Ark Biz 和其余 Ark Plugin 可见。对于 Ark Plugin 来讲,若是须要使用其余 Ark Plugin 的导出类,必须声明为自身的导入类。ui

方法四:阿里的Pandora隔离容器 阿里的Pandora是闭源的,网上资料比较少。 能够从阿里的一次演讲PPT上得知,Pandora仍然是基于ClassLoader实现的 Pandora这类的隔离容器的缺点:spa

image-20180807120821154

  1. 使用方式复杂,难以理解。
  2. 启动慢,用户没法按需选择
  3. 调试困难
  4. 部署和运维困难

下一篇文章将着重分析蚂蚁金服的SOFA-ARK容器的使用和源码分析插件

相关文章
相关标签/搜索