问题描述
同一个对接华为fusioninsight平台的自研软件平台版本,在公司的测试环境中,合并parquet文件功能可以正常执行,但是某现场环境功能失效(其实是合并代码块没有运行)
解决问题过程
1、 通过添加逻辑执行日志的方式,判断合并代码是否执行到。结果发现parquet合并代码块没有执行到,但日志文件中确实没有抛出异常。
2、 parquet合并代码部分,显示抛出的只有IOException,因此代码中仅catch了IOException异常。后来想想又换成了Exception,不过还是日志里还是没有异常日志。最终一狠心,catch Throwable,终于,日志里出现了noclassdeferror、nosuchmethoderror异常。异常信息如下:
2019-04-23 17:32:17,931:INFO threadPool4MergeParquet-6 [com.sinovatio.shark.operator.common.MergeParquetTask:262] - {}
java.lang.NoClassDefFoundError: Could not initialize class org.apache.parquet.CorruptStatistics
at org.apache.parquet.format.converter.ParquetMetadataConverter.fromParquetStatisticsInternal(ParquetMetadataConverter.java:347) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.format.converter.ParquetMetadataConverter.fromParquetStatjava.lang.NoClassDefFoundError: Could not initialize class org.apache.parquet.CorruptStatistics
at org.apache.parquet.format.converter.ParquetMetadataConverter.fromParquetStatisticsInternal(ParquetMetadataConverter.java:347) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.format.converter.ParquetMetadataConverter.fromParquetStatistics(ParquetMetadataConverter.java:361) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.format.converter.ParquetMetadataConverter.fromParquetMetadata(ParquetMetadataConverter.java:817) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.format.converter.ParquetMetadataConverter.readParquetMetadata(ParquetMetadataConverter.java:794) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.hadoop.ParquetFileReader.readFooter(ParquetFileReader.java:484) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.hadoop.ParquetFileReader.<init>(ParquetFileReader.java:568) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.hadoop.ParquetFileReader.open(ParquetFileReader.java:492) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at com.sinovatio.shark.operator.common.MergeParquetTask.run(MergeSmallFilesOperator.scala:231) [shark-jetty.jar:?]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_162]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_162]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_162]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_162]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_162]
2019-04-23 17:32:17,931:INFO threadPool4MergeParquet-2 [com.sinovatio.shark.operator.common.MergeParquetTask:262] - {}
java.lang.NoSuchMethodError: org.apache.parquet.SemanticVersion.<init>(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
at org.apache.parquet.CorruptStatistics.<clinit>(CorruptStatistics.java:45) ~[parquet-column-1.8.2.jar:1.8.1]
at org.apache.parquet.format.converter.ParquetMetadataConverter.fromParquetStatisticsInternal(ParquetMetadataConverter.java:347) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.format.converter.ParquetMetadataConverter.fromParquetStatistics(ParquetMetadataConverter.java:361) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.format.converter.ParquetMetadataConistics(ParquetMetadataConverter.java:361) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.format.converter.ParquetMetadataConverter.fromParquetMetadata(ParquetMetadataConverter.java:817) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.format.converter.ParquetMetadataConverter.readParquetMetadata(ParquetMetadataConverter.java:794) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.hadoop.ParquetFileReader.readFooter(ParquetFileReader.java:484) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.hadoop.ParquetFileReader.<init>(ParquetFileReader.java:568) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at org.apache.parquet.hadoop.ParquetFileReader.open(ParquetFileReader.java:492) ~[parquet-hadoop-1.8.2.jar:1.8.2]
at com.sinovatio.shark.operator.common.MergeParquetTask.run(MergeSmallFilesOperator.scala:231) [shark-jetty.jar:?]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_162]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_162]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_162]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_162]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_162]
3、 百度搜索noclassdeferror,没有提供什么解决方法,只是说运行期间,找不到该class。然后就将注意力转移到nosuchmethoderror异常。
4、 利用grep -R “org.apache.parquet.SemanticVersion”*,查找该类出现在哪些jar中,parquet-hadoop-bundle-1.8.1.jar和parquet-common-1.8.2.jar中均包含org.apache.parquet.SemanticVersion类。
5、 jinfo 进程号命令,查看进程加载了哪些jar(该步骤可不用)
6、 借助阿里arthas工具,查看进程中加载该类来自于哪个jar包;arthas使用方式如下:
- java -jar arthas-boot.jar 显示了本机上所有的java进程
- 输入java进程前面的序列号,并回车,进入交互命令行
- sc -d org.apache.parquet.SemanticVersion,显示该类,来自于哪个jar。
最终发现来自于parquet-hadoop-bundle-1.8.1.jar,该包中的org.apache.parquet.SemanticVersion类没有提供接受三个string参数的构造器,而parquet-common-1.8.2.jar包中提供了接受三个string参数的构造器。
解决方法
将parquet-hadoop-bundle-1.8.1.jar升级为parquet-hadoop-bundle-1.8.2.jar,因为1.8.2中不再提供org.apache.parquet.SemanticVersion类,进程就直接去加载parquet-common-1.8.2.jar中的org.apache.parquet.SemanticVersion类。
遗留问题
同一套代码,lib包下的jar个数都一样,且parquet相关的jar版本也是一样的,公司测试环境为什么就可以执行成功?classloader加载lib的先后顺序(规则)如何?