测试应用说明

测试的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包

最新文章

  1. 【codeforces 442B】 Andrey and Problem
  2. Unity学习疑问记录之 动作动画忽略timeScale
  3. 获取iPhone 联系人列表,并且根据分析得到的姓名首字母进行排序
  4. 每周一书-2016年8月15日到21日(bootstrap基础教程)获奖读者公布
  5. 在ASP.NET 5中如何方便的添加前端库
  6. jdk 1.8 Executors
  7. Oracle中PL/SQL简介、基本语法以及数据类型
  8. static方法不能直接访问类内的非static变量和不能调用this,super语句分析
  9. java—— 编译与运行
  10. android.view.InflateException: Binary XML file line #7: Error inflating class(OOM)
  11. WCF基于MSMQ的事件代理服务
  12. EFCore扩展:IQueryable(linq)或sql执行的查询缓存与清理
  13. Leetcode_121_Best Time to Buy and Sell Stock
  14. CodeForces 140C New Year Snowm
  15. pp 总结一
  16. jvm字节占用空间分析
  17. IT 产品 需求 痛点
  18. 虚拟主机是设置在httpd-vhosts.conf还是vhosts.conf还是httpd.conf
  19. Git常见使用方法
  20. windows网卡共享网络时,报Internet连接共享访问被启用时,出现了一个错误。(null)

热门文章

  1. 有语言基础的人应该如何学习python?
  2. Vue--el-menu 的自动跳转功能与自己的click事件冲突
  3. js精确到指定位数的小数
  4. linux下利用JMX监控Tomcat
  5. SaltStack 任意文件读写漏洞(CVE-2020-11652)
  6. 解决ModuleNotFoundError: No module named &#39;pip&#39;问题
  7. awk文本
  8. Docker入门第一章
  9. 面试官:实现一个带值变更通知能力的Dictionary
  10. Shell-03-表达式和运算符