Hadoop 3实战指南
上QQ阅读APP看书,第一时间看更新

1.3.1 单元测试

在源码的根目录中有个BUILDING.txt文件,其中记录了编译Hadoop所需的软件及一些步骤。由于只是为了进行单元测试和通过断点的方式调试代码,因此这里只编译Hadoop,并没有生成对应的二进制文件,编译命令为mvn package -Psrc -DskipTests。如果要部署Hadoop,还需要将源码编译成二进制文件。编译二进制文件时,需要先安装一些依赖软件,具体如下(这里只关注Linux/Mac环境下的编译步骤,Windows环境下大同小异)。

  • JDK 1.8
  • Maven 3.3及以上版本
  • Protocol Buffer 2.5.0
  • CMake 3.1及以上版本
  • zlib-devel
  • cyrus–sasl-devel
  • GCC 4.8.1及以上版本
  • openssl-devel
  • Linux FUSE 2.6及以上版本
  • Jansson
  • Doxygen
  • Python
  • bats
  • Node.js

关于它们的具体安装方式,网上有很多教程,并且较为简单,故不在此展开。

成功安装上述依赖软件之后,执行编译命令mvn clean package -DskipTests -Pdist, native -Dtar,正常情况下会在hadoop-dist/target下生成一个Hadoop的二进制tar包。如果没有成功,也不要慌,执行mvn clean package -DskipTests -Pdist,native -Dtar -X查看具体是哪儿出错了,然后再针对性地去解决。我是在Mac环境下编译的,如果在编译过程中遇到问题,可以参考我的博客。在Linux环境下,如果依赖软件都安装正确,则出错的概率较小。

将编译成功的Hadoop代码按照本节开头介绍的方式导入IDEA,然后随便找个测试类执行单元测试,看看是否有异常信息。这里执行TestJob类,如果成功,则表示源码阅读环境已基本部署成功(如果出现有些模块未识别成Maven项目的情况,可以参考图1-6进行修改)。

之所以说基本部署成功,是因为还可能会遇到webapps/datanode或者webapps/hdfs的异常,错误信息为java.io.FileNotFoundException: webapps/datanode not found in CLASSPATH。如果上述步骤都正常,解决这个问题将非常容易。根据报错信息可以发现,这是HttpServer2类抛出的异常,此时只要在代码处加入一些代码进行调试即可解决,具体代码如下:

protected String getWebAppsPath(String appName) throws FileNotFoundException {
  URL resourceUrl = null;
  // 将webResourceDevLocation赋值为src/main/webapps目录
  File webResourceDevLocation = new File("src/main/webapps", appName);
  // 在此处加入代码,打印出具体的目录信息
  System.out.println(webResourceDevLocation.getAbsolutePath());
  // 判断Web服务器的运行方式
  // 当webResourceDevLocation不存在时,运行模式为开发模式
  if (webResourceDevLocation.exists()) {
    LOG.info("Web server is in development mode. Resources "
      + "will be read from the source tree.");
    try {
      resourceUrl =
        webResourceDevLocation.getParentFile().toURI().toURL();
    } catch (MalformedURLException e) {
      throw new FileNotFoundException("Mailformed URL whilefinding the
        " + "web resource dir:" + e.getMessage());
    }
  } else {
    resourceUrl =
      getClass().getClassLoader().getResource("webapps/" + appName);
    // 具体的报错信息是从这里报出的
    if (resourceUrl == null) {
      throw new FileNotFoundException("webapps/" + appName +
        " not found in CLASSPATH");
    }
  }
  String urlString = resourceUrl.toString();
  return urlString.substring(0, urlString.lastIndexOf('/'));
}

查看代码,会发现报错信息在else语句块中。这段代码的功能是得到Web服务器的路径,其中有一个if/else语句块,用于判断Web服务器的运行模式。当webResourceDevLocation.exists()为真,即webResourceDevLocation对应的目录存在时,代表运行的是开发模式,进入if语句块;不为真时进入else语句块,报错的原因是webResourceDevLocation对应的目录不存在。进入else语句块后,resourceUrl未获取到对应的值,为null,即判断条件resourceUrl == null为真,然后抛出异常。由于这里是在IDEA中运行开发模式,因此只创建webResourceDevLocation对应的目录即可。至于具体在哪里创建目录,只需在if语句之前加入println语句,查看Web服务器使用的相对目录是什么,然后在相对目录中创建src/main/webapps目录即可。在实际操作过程中,可能还会报其他异常,也可按照此思路一一解决。

注意:如果导入IDEA的源码不是通过mvn package -Psrc -DskipTests进行编译的,那么执行单元测试时就没有上面这么顺利了。比如会出现找不到proto类,或者找不到AvroRecord类的问题,但这些都容易解决,通过对序列化的文件进行编译,自动生成对应的Java文件,然后将其复制到对应的包目录即可。比较麻烦的是,有些模块中找不到另一个模块的类,这就需要更改当前模块对其依赖的对应模块的依赖范围。例如,导入的代码是未通过mvn clean package -DskipTests -Pdist,native -Dtar编译成功的,运行单元测试时,有可能会报下面的异常信息:

/Users/xx/tmp/hadoop-3.2.0-src/hadoop-common-project/hadoop-auth/src/test/java/org
  /apache/hadoop/security/authentication/server/TestKerberosAuthenticationHandler.java
Error:(16, 33) java: 程序包org.apache.hadoop.minikdc不存在
Error:(48, 13) java: 找不到符号
  符号: 类 KerberosSecurityTestcase
Error:(81, 5) java: 找不到符号
  符号:   方法 getKdc()
  位置: 类 org.apache.hadoop.security.authentication.
    server.TestKerberosAuthenticationHandler
Error:(186, 5) java: 找不到符号
  符号:   方法 getKdc()
  位置: 类 org.apache.hadoop.security.authentication.server.
    TestKerberosAuthenticationHandler

此时就需要将auth模块对minikdc模块的依赖范围改为Compile或者Provided,如图1-7所示。

图1-7 修改依赖模块的范围