提交Spark作業遇到的NoSuchMethodError問題總結


測試應用說明

測試的Spark應用實現了同步hive表到kafka的功能。具體處理流程:

  • 從 ETCD 獲取 SQL 語句和 Kafka 配置信息
  • 使用 SparkSQL 讀取 Hive 數據表
  • 把 Hive 數據表的數據寫入 Kafka

應用使用etcd來存儲程序所需配置,通過拉取etcd的kv配置,來初始化sql語句和kafka配置的參數。

提交方式及相應的問題

  • 使用client模式,提交無依賴的jar包

提交命令

 /usr/local/spark-2.3.0-bin-2.8.2/bin/spark-submit \
    --name hive2kafka \
    --master yarn \
    --deploy-mode client \
    --driver-cores 1 \
    --driver-memory 2g \
    --num-executors 2 \
    --executor-cores 1 \
    --executor-memory 2g \
    --queue hive \
    --class com.ljh.spark.Hive2Kafka  \
    /data0/jianhong1/demo-v25/target/demo-1.0-SNAPSHOT.jar

應用運行失敗,driver端報錯:

Exception in thread "main" java.lang.NoClassDefFoundError: io/etcd/jetcd/Client
	at com.ljh.spark.EtcdUtil.getClient(EtcdUtil.java:27)
	at com.ljh.spark.EtcdUtil.get(EtcdUtil.java:46)
	at com.ljh.spark.Hive2Kafka.main(Hive2Kafka.java:60)
	...
Caused by: java.lang.ClassNotFoundException: io.etcd.jetcd.Client
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

從報錯信息可以看出,driver端沒有查找到etcd的某個類,即沒有加載到etcd 的jar包。說明spark應用driver進程的classpath不包含etcd 的jar包。因此考慮打包fat jar,把etcd的jar包打入用戶提交的jar。

  • 使用client模式,提交包含依賴的jar包

提交命令

/usr/local/spark-2.3.0-bin-2.8.2/bin/spark-submit \
    --name hive2kafka \
    --master yarn \
    --deploy-mode client \
    --driver-cores 1 \
    --driver-memory 2g \
    --num-executors 2 \
    --executor-cores 1 \
    --executor-memory 2g \
    --queue hive \
    --class com.ljh.spark.Hive2Kafka  \
    /data0/jianhong1/demo-v25/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar

應用運行失敗,driver端報錯:

Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;CLjava/lang/Object;)V
	at io.grpc.Metadata$Key.validateName(Metadata.java:742)
	at io.grpc.Metadata$Key.<init>(Metadata.java:750)
	at io.grpc.Metadata$Key.<init>(Metadata.java:668)
	at io.grpc.Metadata$AsciiKey.<init>(Metadata.java:959)
	at io.grpc.Metadata$AsciiKey.<init>(Metadata.java:954)
	at io.grpc.Metadata$Key.of(Metadata.java:705)
	at io.grpc.Metadata$Key.of(Metadata.java:701)
	at io.etcd.jetcd.ClientConnectionManager.<clinit>(ClientConnectionManager.java:69)
	at io.etcd.jetcd.ClientImpl.<init>(ClientImpl.java:37)
	at io.etcd.jetcd.ClientBuilder.build(ClientBuilder.java:401)
	at com.ljh.spark.EtcdUtil.getClient(EtcdUtil.java:28)
	at com.ljh.spark.EtcdUtil.get(EtcdUtil.java:46)
	at com.ljh.spark.Hive2Kafka.main(Hive2Kafka.java:60)
    ...

從報錯信息可以看出,應用沒有找到guava包Preconditions類checkArgument方法 。說明程序找到了guava包Preconditions類,但是這個類沒有找到checkArgument的某個構造方法。這種問題一般是由於jar包沖突,即程序加載了低版本的jar包,但是程序需要調用高版本jar包的某個方法,而這個方法低版本中沒有,就會出現上面的報錯NoSuchMethodError

因此考慮把程序中沖突的低版本guava包排除掉。通過檢查程序pom文件的jar包依賴,明確添加適配etcd高版本的guava包,並把沖突的低版本的guava包排除掉。重新運行,發現依然出現上面的NoSuchMethodError報錯。

因此猜測低版本的guava包不是由於程序代碼引入的,而是由spark提交機的本地包引入的。通過檢查spark提交機的本地包,查到引入了guava-14.0.1.jar,而程序中etcd依賴的guava包需要的版本為20+。說明應用使用了本地jar的低版本guava類,而沒有使用fat-jar的高版本guava類。由此推測出,spark應用driver端的類加載優先級:本地jar > fat-jar。

  • 使用client模式,提交包含依賴的jar包,並添加driver-class 類路徑

提交命令

/usr/local/spark-2.3.0-bin-2.8.2/bin/spark-submit \
    --name hive2kafka \
    --master yarn \
    --deploy-mode client \
    --driver-class-path /data0/jianhong1/demo-v25/target/lib/guava-23.6-jre.jar:/data0/jianhong1/demo-v25/target/lib/protobuf-java-3.5.1.jar \
    --driver-cores 1 \
    --driver-memory 2g \
    --num-executors 2 \
    --executor-cores 1 \
    --executor-memory 2g \
    --queue hive \
    --class com.ljh.spark.Hive2Kafka  \
    /data0/jianhong1/demo-v25/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar

程序正常運行,不再出現NoSuchMethodError報錯。由此推測出,spark應用driver端的類加載優先級:driver-class-path 配置 > 本地jar。

  • 使用cluster模式,提交包含依賴的jar包

提交命令

/usr/local/spark-2.3.0-bin-2.8.2/bin/spark-submit \
    --name hive2kafka \
    --master yarn \
    --deploy-mode cluster \
    --driver-cores 1 \
    --driver-memory 2g \
    --num-executors 2 \
    --executor-cores 1 \
    --executor-memory 2g \
    --queue hive \
    --class com.ljh.spark.Hive2Kafka  \
    /data0/jianhong1/demo-v25/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar

應用運行失敗,報錯信息:

	 diagnostics: User class threw exception: java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;CLjava/lang/Object;)V
	at io.grpc.Metadata$Key.validateName(Metadata.java:742)
	at io.grpc.Metadata$Key.<init>(Metadata.java:750)
	at io.grpc.Metadata$Key.<init>(Metadata.java:668)
	at io.grpc.Metadata$AsciiKey.<init>(Metadata.java:959)
	at io.grpc.Metadata$AsciiKey.<init>(Metadata.java:954)
	at io.grpc.Metadata$Key.of(Metadata.java:705)
	at io.grpc.Metadata$Key.of(Metadata.java:701)
	at io.etcd.jetcd.ClientConnectionManager.<clinit>(ClientConnectionManager.java:69)
	at io.etcd.jetcd.ClientImpl.<init>(ClientImpl.java:37)
	at io.etcd.jetcd.ClientBuilder.build(ClientBuilder.java:401)
	at com.ljh.spark.EtcdUtil.getClient(EtcdUtil.java:28)
	at com.ljh.spark.EtcdUtil.get(EtcdUtil.java:46)
	at com.ljh.spark.Hive2Kafka.main(Hive2Kafka.java:66)
    ...

從報錯信息可以看出,應用找到了guava包Preconditions類,但是在這個類中沒有找到checkArgument的某個構造方法。

因此考慮在提交作業時明確指出etcd所依賴的高版本guava包。於是提交參數添加了 --jars hdfs:/user/jianhong1/jars/guava-23.6-jre.jar,hdfs:/user/jianhong1/jars/protobuf-java-3.5.1.jar \,重新運行后依然報上面的錯。說明 --jar 參數只是負責把jar包拷貝到運行作業的服務器上,但是沒把指定的jar包加到類路徑。

  • 使用cluster模式,提交包含依賴的jar包,並添加driver 和executor 類路徑。

提交命令

/usr/local/spark-2.3.0-bin-2.8.2/bin/spark-submit \
    --name hive2kafka \
    --master yarn \
    --deploy-mode cluster \
    --driver-cores 1 \
    --driver-memory 2g \
    --num-executors 2 \
    --executor-cores 1 \
    --executor-memory 2g \
    --queue hive \
    --class com.ljh.spark.Hive2Kafka  \
    --conf spark.driver.extraClassPath=guava-23.6-jre.jar:protobuf-java-3.5.1.jar \
    --conf spark.executor.extraClassPath=guava-23.6-jre.jar:protobuf-java-3.5.1.jar \
    --jars hdfs:/user/jianhong1/jars/guava-23.6-jre.jar,hdfs:/user/jianhong1/jars/protobuf-java-3.5.1.jar \
    /data0/jianhong1/demo-v25/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar

通過增加guava包的driver 和executor 類路徑的配置后,應用成功運行!

總結

本文介紹了client 方式和cluster 方式提交Spark 應用時所遇到的NoSuchMethodError 問題,及相應的解決方案。通過實際測試得到結論: spark應用的類加載優先級:--driver-class-path--executor-class-path配置 > 本地jar > fat-jar。

參考

The --jars argument only transports the jars to each machine in the cluster. It does NOT tell spark to use them in the class path search. The --driver-class-path (or similar arguments or config parameters) are also required.
--jars 參數只是用於傳輸 jar 包到集群的 Executor 和 Driver 的服務器上,它不會告知 spark 應用在哪個類路徑下使用這些jar包。因此,--driver-class-path或--executor-class-path參數也是必需的,用於配置 driver 和 executor 的類路徑。  
spark on yarn運行時會加載的jar包有如下:
spark-submit中指定的--jars
$SPARK_HOME/jars下的jar包
yarn提供的jar包
spark-submit通過參數spark.driver/executor.extraClassPath指定的jar包


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM