衆所周知,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-client
和elasticsearch-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
同時在ChildFirstClassLoader
和AppClassLoader
中加載,在調用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
事發代碼
分析:
實參 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,實測配置有效,但因爲類加載連帶關係複雜,沒法窮舉,因此做罷。