2018 的最后一个工作日, 是在调式和诊断问题的过程中度过, 原本可以按时下班, 毕竟最后一天了, 然鹅, 确是一直苦苦挣扎.
废话不多说, 先描述一下问题: 有一套大数据环境, 是 CDH 版本的, 总共 4 台机子, 我们的应用程序与大数据集群之前已经集成完毕, 调试没有问题, 可以运行 Spark 任务. 而与这个集群集成是 17 年下半年的事了, 这次升级后, 发现无法正确的执行任务, 不管是程序提交的还是用示例程序 SparkPi, 或者手动用 spark-submit 提交, 都是执行失败, 且 Yarn 框架调度执行两次. 主要错误提示如下:
Diagnostics: java.lang.NoSuchMethodError: org.apache.commons.io.FileUtils.isSymlink(Ljava/io/File;)Z
截图如下:
sprak-submit 提交客户端日志截图
yarn:8088 任务监控页面任务 (与上图不是一个任务, 但错误信息一致)
其实, 从任务日志错误提示, 可以看出, 肯定是加载了低版本的包, 用了高版本的方法, NoSuchMethod 异常再也熟悉不过了, 这一点确信后, 就需要找这个包在哪里? FileUtils 类属于 commons-io 包, 目前有 1.4 和 2.4 版本的, 其中, 1,4 中的 FileUtils 类确实没有 isSymlink 方法. 好了, 基本可以确认, 程序在使用了这个 commons-io-1.4.jar 包.
虽然问题确认, 但是还是让人很头疼, 因为我可以确认, 我们自己的应用程序中, 只用了 2.4 的包. 为了排序一些常规影响, 我们分别使用 cdh 自带的 sparka 提交, 使用最简单的 SparkPi 来运行, 但都运行失败, 并且我还写了一个搜索类所在的包的工具, 来运行排查问题.
- package com.meritdata.search.tool;
- import java.io.PrintStream;
- import java.security.CodeSource;
- import java.security.ProtectionDomain;
- public class Utils
- {
- public static void search(String name)
- {
- try
- {
- Class clazz = Class.forName(name);
- ProtectionDomain pd = clazz.getProtectionDomain();
- CodeSource cs = pd.getCodeSource();
- System.out.println(name + "location:" + cs.getLocation());
- }
- catch (Throwable e) {
- e.printStackTrace();
- }
- }
- }
- package com.meritdata.search.tool;
- public class Search
- {
- public static void main(String[] args)
- {
- String name = args[0];
- Utils.search(name);
- }
- }
这是最先开始使用的代码, 想验证当前任务加载的包是不是有其他的版本, 使用提交命令:
%SPARK_HOME%>.\bin\spark-submit.cmd --master --deploy cluster --files hive-site.xml --class com.meritdata.search.tool.Search search.tool-0.0.1-SNAPSHOT.jar org.apache.commons.io.FileUtils
这个任务运行的结果, 打印的是 commons-io-2.4.jar. 确认后再想, 是不是其他地方引用, 而这个 demo 比较简单, 没有使用到, 因为加载顺序不一样, 可能会影响. 所以, 又完善了一下程序:
- package com.meritdata.search.tool;
- import java.util.Collections;
- import org.apache.spark.sql.Dataset;
- import org.apache.spark.sql.Encoders;
- import org.apache.spark.sql.SparkSession;
- import org.apache.spark.sql.SparkSession.Builder;
- public class SparkDemo
- {
- public static void main(String[] args)
- {
- try
- {
- SparkSession spark = SparkSession.builder().appName("SparkDemo").enableHiveSupport().getOrCreate();
- Dataset dataset = spark.createDataset(Collections.singletonList("test"), Encoders.STRING());
- dataset.show();
- spark.sql("SELECT explode(Array(\"a\", \"b\", \"c\")) as v").show();
- } catch (Throwable e) {
- e.printStackTrace();
- }
- String name = args[0];
- Utils.search(name);
- }
- }
因为实际中, 要使用 spark, 必须初始化 SparkSession, 这样运行完成 spark 的代码后, 再看看打印的包名称. 但是结果令人意外, 在初始化 SparkSession 的时候, 任务就异常了, 错误信息如开头提示. 这下, 我终于确认, 是 Yarn 服务加载了不该加载的 jar 包, 这下, 就要排查 yarn 加载的包了, 除过应用自己, yarn 还要加载默认的包, hadoop 的, hdfs 的, yarn 的包, 配置项为 yarn.application.classpath, 在 CDH 的配置界面可以搜索到, 有默认配置, 会加载 hadoop 的一些基础程序包. 首先想到的是, 肯定是这个路径下面加载了低版本的包, 想清空试试运行, 结果还不行, 因为有默认值, 不填写每用, 后来才想到的, 因为当时已经调试的懵逼了.
发现不过后, 想着去服务器上面, 看看那些地方都有 1.4 的包, 使用搜索命令:
find / -name commons-io-*.jar -type f
搜索结果如图:
在大数据集群的某个节点搜索到的结果
从搜索结果来看, 在 cdn 的 jars 下面有个 1.4 的包, 我想, 会不会是这样包引起的呢? 但是, 我在其他的环境中也发现这个包, 不可能是这个包引起的, 要不然, 都得有问题. 既然这个包没有影响, 任务加载的包也是正确的, 哪到底是哪里的影响了任务的执行?
此时, 这个问题已经搞得人头疼, 尝试了却没有发现问题, 明明很简单, 就包冲突, 却无法解决. 在领导的提示下, 替换 1.4 中的 FileUtils 类, 看看到底是不是这个包的问题, 也不管了, 只能试试了, 司马当活马医. 修改完成, 打包, 在四个节点搜索所有的 1.4 包, 准备替换.
突然, 发现了一个让人惊喜的意外, 在大数据集群的某个节点, 在 Hadoop 的子目录, 搜索到了一个 1.4 的包, 而且不是软链接.
从搜索结果可以看出来, 在执行的时候, 每个任务下临时目录会复制 jar 包, hadoop/lib/commons-io-1.4.jar 就是这个原因, 查看其他节点, 均为有类似情况. 毫无疑问, 删除这个包, 重启集群, 执行 demo, 成功, 终于松了一口气. 不知道是什么原因导致, 现场环境复杂, 所以也没办法追究, 也没有必要, 问题出现, 就得解决. 其实, 很早之前, 就应该检查四个节点, 这样的话, 就不会在那瞎想, 可以尽早的发现异常包.
由于是客户现场, 调试不方便, 同事也比较辛苦, 在现场的滋味, 我深有体会. 问题虽然简单, 但是对整理的了解和调试方法也是很重要的, 否则, 会做很多无用功.
仅此来纪念和告别 2018 的辛勤工作, 2019 继续奋发前行, 让自己更强.
本文地址: https://www.cnblogs.com/flowerbirds/p/10205185.html
来源: https://www.cnblogs.com/flowerbirds/p/10205185.html