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 修改依赖模块的范围