1. 問題
在工作中,多次遇到了jar包沖突的情況,就以這個為例,整理下解決思路。
現有某Spark程序,從Elasticsearch中讀取數據進行后續的各種分析。當Spark版本是2.0.0,Elasticsearch的版本是5.2的時候,程序能夠正常運行;后來由于項目需要,將Elasticsearch版本升級到了6.5,而Spark版本依然是2.0.0,此時出現了以下情況,異常如下:
2. 問題分析和解決方案
上述異常其實是NoSuchMethodError,jar包沖突還表現為ClassNotFoundException、NoClassDefFoundError等情況。
針對本次遇到的NoSuchMethodError的問題,主要是兩種情況引起的:
- 存在該類,但是類中的方法不存在;
- 存在多個包含該類的jar包,造成沖突。
分析如下:
2.1 查看該類在哪個jar包下
本次使用的是Eclipse進行開發的,可以按住快捷鍵 Ctr+Shift+t 查找項目中引用的jar包中哪些包含該類,彈出來如下窗口,輸入異常中的類名:io.netty.buffer.CompositeByteBuf
從檢索結果中可以看出,netty-buffer-4.1.30.Final.jar和netty-all-4.0.29.Final.jar都包括這個類。
在依賴包下看到引入順序是先引入netty-all-4.0.29.Final.jar再引入netty-buffer-4.1.30.Final.jar
點進去每個jar包看了下該類下的方法,發現兩個jar包都有報錯中的方法存在,這就很奇怪了,修改的方法有兩種:
1)將低版本刪除,也就是刪除了netty-all-4.0.29.Final.jar ,再次運行程序正常;
2)將netty-buffer-4.1.30.jar的引用順序挪到netty-all-4.0.29.jar的上面,問題得以解決。
此時local 模式異常得以解決。
此處有一點不明白為什么都包含該方法卻提示方法不存在。有沒有大神可以解釋下。
如果是在Linux環境,可以采用如下命令檢測某個類是否在某個jar包下:
jar -tf xxxxx.jar | grep "io.netty.buffer.CompositeByteBuf"
同樣可以采用反編譯軟件jd-gui反編譯jar包,查看是否包含某個類和類中的方法。
2.2 Spark Standalone集群運行異常
將程序打包到Spark集群,啟動方式為Standalone-client模式,腳本如下:
就出現一個問題,上面解決方案中刪除的是netty-all-4.0.29.Final.jar或者調整引用順序,這個包其實是Spark2.0所依賴的包;
我們知道yarn集群的Spark程序是先加載Spark集群上每個節點的{Spark_HOME}/jar的包,再加載程序中依賴的其他jar包。為了保證Spark下低版本的netty不會覆蓋程序中ES依賴的高版本netty,可以采用配置啟動參數,保證先加載用戶程序依賴的jar包:
spark.executor.userClassPathFirst=true
spark.driver.userClassPathFirs=true
運行之后,出現異常如下:
異常棧里的類io.netty.util.ReferenceCountUtil檢測了下是存在netty-all-4.0.29.Final.jar類中的,由于上面采用高版本的netty4.1覆蓋了低版本的netty4.0導致了這種問題出現,最終的解決方案是升級Spark到2.3.0版本,該版本的Spark的netty包和Elasticsearch的netty包都是4.1版本。問題得以解決。如下圖為maven repository下Spark2.3.0運行庫的jar包。