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容器的使用和源碼分析插件

相關文章
相關標籤/搜索