java多類加載器類衝突案例分析

衆所周知,jvm類加載機制採用雙親委派機制。但在有些框架中,經常爲了提供某種形式的「隔離和沙盒」,自定義一種稱爲ChildFirst的了類加載器,簡單的說就是破壞了雙親委派,由自定義子類加載器優先加載類,而不是先委派給父加載器。因爲同一個類能夠在不一樣的類加載器中分別加載,使用ChildFirst機制,可讓類加載器造成一個「沙盒」,在程序中同時運行兩個相同但不一樣版本的類。html

可是,筆者遇到一個罕見的類加載衝突的案例,根因與ChildFirst機制有關。java

原由

程序在flink平臺上運行,將數據寫入es,而某平臺在開啓了安全機制後,整個平臺包括es都須要基於kerberos認證來訪問。基於先驗的結論,須要替換魔改的elasticsearch-rest-client,其中使用GSSAPI登陸了kerberos,並基於SENGPO協議,經過http發送了類token,還有一個獨立的線程對token進行刷新。因爲是個private jar,在工程中使用很是不友好,因此考慮在打包job的時候用maven-shade-plugin排除原先依賴的elasticsearch-rest-clientelasticsearch-rest-highlevel-client,而將定製版jar放到flink/lib目錄下。git

報錯

提交做業後,Task Manager報錯退出以下:github

java.lang.LinkageError: loader constraint violation: when resolving method "org.elasticsearch.client.RestClient.builder([Lorg/apache/http/HttpHost;)Lorg/elasticsearch/client/RestClientBuilder;" the class loader (instance of org/apache/flink/util/ChildFirstClassLoader) of the current class, org/apache/flink/streaming/connectors/elasticsearch6/Elasticsearch6ApiCallBridge, and the class loader (instance of sun/misc/Launcher$AppClassLoader) for the method's defining class, org/elasticsearch/client/RestClient, have different Class objects for the type [Lorg/apache/http/HttpHost; used in the signature

錯誤描述的意思是:apache

org/apache/http/HttpHost同時在ChildFirstClassLoaderAppClassLoader中加載,在調用org.elasticsearch.client.RestClient.builder的時候傳入org/apache/http/HttpHost實例的時候發現,方法簽名(形參)中的類org/apache/http/HttpHost屬於AppClassLoader,而實參的org/apache/http/HttpHost卻屬於ChildFirstClassLoader,形成了衝突。安全

下面這兩篇文章能夠參考一下:框架

https://www.cnblogs.com/deepnighttwo/archive/2011/08/31/2160990.htmljvm

https://bigzuo.github.io/2017/03/19/java-LinkageError-loader-constraint-violation-error/elasticsearch

緣由分析

基於下面這些事實:maven

  1. job包中把HttpHost類打進去了。
  2. flink的tm進程啓動的時候會將hadoop下的httpcore.x.x.x.jar加到classpath
  3. AppClassLoader負責加載classpath參數裏面的類
  4. ChildFirstClassLoader負責加載job包中的類

事發代碼

image.png

分析:

實參 httpHosts 是序列化到tm上的List<HttpHost>,也是Elasticsearch6ApiCallBridge這個類(在job包中)的私有屬性。這意味着實參 httpHosts的類 優先由 ChildFirstClassLoader加載,且基於事實1,ChildFirstClassLoader能加載到HttpHost

RestClient類位於elasticsearch-rest-client,即在flink/lib目錄,因爲咱們打包的時候不會將elasticsearch-rest-client打進來,因此ChildFirstClassLoader沒法加載到這個類,只能由AppClassLoader加載,並且因爲事實2,AppClassLoader也能加載到HttpHost

這麼一來,就會出現上面報錯

解決方案

最初咱們經過將flink改成parent-first,能解決。通過分析:這是由於ParentFirstClassLoader不會先從job包中加載HttpHost,轉而由AppClassLoader加載HttpHost,這樣就不會有衝突。

從上述根因分析,還有一個方案是將elasticsearch-rest-client等相關jar也打包到job裏面,這樣保證所有由ChildFirstClassLoader加載。不過這樣引入private jar,形成了版本管理的混亂。

flink支持classloader.parent-first-patterns.additional,在child-first的前提下,對某些類作parent-first,實測配置有效,但因爲類加載連帶關係複雜,沒法窮舉,因此做罷。

相關文章
相關標籤/搜索