本文基于hadoop3.0.3版本
调试环境:
OS: CentOS Linux release 7.5.1804 (Core)(包含桌面,便于使用开发工具)
maven: Apache Maven 3.5.4
jdk: 1.8.0_151
IDE:IntelliJ idea-IU-181.5281.24
在上一篇文章中,我们完成了基于hadoop3.0.3版本的源码编译与打包,并可以通过idea加载源码工程,以下将在使用idea正确打开hadoop源码的前提下进行
Standalone模式
该模式下无需启动任何服务,可直接基于本地文件执行mapreduce程序,方便进行源码调试。分析并调试hdfs以及yarn组件,我们将在后文中以伪分布式模式进行
1.任务提交流程
我们以官网提供的样例mapreduce程序为例
mkdir input
cp etc/hadoop/*.xml input
bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.0.3.jar grep input output ‘dfs[a-z.]+’
cat output/*
在上一篇文章中已演示了以上操作,并成功取得执行结果,现在以bin/hadoop命令作为切入点,分析该程序的执行过程
首先找到源码中hadoop命令的位置,我们定位到
hadoop-common-project/hadoop-common/src/main/bin 目录,该目录下包含hadoop客户端的一些脚本命令
为了不陷入细节,我们从宏观上理解一下重点脚本的用途,再选择我们关注的主线继续往下分析
**hadoop:**该命令是一个shell脚本,也是所有客户端命令的入口,该脚本主要负责以下工作
1.初始化hadoop工程所需的环境,包含各种环境变量,依赖路径等
2.解析命令行传入的参数
3.通过解析的参数调用指定的java类
hadoop-config.sh: 该脚本负责加载hadoop工程所需的各种环境,在hadoop命令中被调用
hadoop-functions.sh: 该脚本负责封装各种shell函数,被其他脚本所调用
其他脚本在本次程序运行中暂不涉及
现在追踪到hadoop命令的第215行
当我们在执行bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.0.3.jar grep input output ‘dfs[a-z.]+’ 这条命令时,会执行hadoopcmd_case函数,并传入相关参数,打开hadoop_cmd_case函数:
在第139行可以看到当我们执行hadoop jar命令时,HADOOP_CLASSNAME变量被设置为了org.apache.hadoop.util.RunJar类
然后定位到hadoop命令的最后一行,可以看到一个函数,依次追踪该函数的调用链可以看到最后执行该HADOOP_CLASSNAME的代码,调用关系为hadoop_generic_java_subcmd_handler(hadoop 228行) ⇒ hadoop_java_exec(hadoop-functions.sh 2674行)
function hadoop_java_exec
{
# run a java command. this is used for
# non-daemons
local command=$1
local class=$2
shift 2
hadoop_debug "Final CLASSPATH: ${CLASSPATH}"
hadoop_debug "Final HADOOP_OPTS: ${HADOOP_OPTS}"
hadoop_debug "Final JAVA_HOME: ${JAVA_HOME}"
hadoop_debug "java: ${JAVA}"
hadoop_debug "Class name: ${class}"
hadoop_debug "Command line options: $*"
export CLASSPATH
#shellcheck disable=SC2086
exec "${JAVA}" "-Dproc_${command}" ${HADOOP_OPTS} "${class}" "$@"
}
该函数的最后一行使用java命令调用了org.apache.hadoop.util.RunJar;类,并将执行的mapreduce jar包,执行入口等作为参数传入,我们增加一些打印内容,详细观察下完整的执行命令
export CLASSPATH
#shellcheck disable=SC2086
echo %CLASSPATH
echo ${JAVA}
echo ${command}
echo ${HADOOP_OPTS}
echo ${class}
echo $@
exit
exec "${JAVA}" "-Dproc_${command}" ${HADOOP_OPTS} "${class}" "$@"
以下为输出结果,这里我们看到了使用hadoop jar执行mapreduce任务的完整执行命令
[LIN@localhost hadoop-3.0.3]$ vi libexec/hadoop-functions.sh
[LIN@localhost hadoop-3.0.3]$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.0.3.jar grep input output 'dfs[a-z.]+'
/home/LIN/software/hadoop-3.0.3/etc/hadoop:/home/LIN/software/hadoop-3.0.3/share/hadoop/common/lib/*:/home/LIN/software/hadoop-3.0.3/share/hadoop/common/*:/home/LIN/software/hadoop-3.0.3/share/hadoop/hdfs:/home/LIN/software/hadoop-3.0.3/share/hadoop/hdfs/lib/*:/home/LIN/software/hadoop-3.0.3/share/hadoop/hdfs/*:/home/LIN/software/hadoop-3.0.3/share/hadoop/mapreduce/lib/*:/home/LIN/software/hadoop-3.0.3/share/hadoop/mapreduce/*:/home/LIN/software/hadoop-3.0.3/share/hadoop/yarn/lib/*:/home/LIN/software/hadoop-3.0.3/share/hadoop/yarn/*
/usr/java/bin/java
jar
-Djava.net.preferIPv4Stack=true -Dyarn.log.dir=/home/LIN/software/hadoop-3.0.3/logs -Dyarn.log.file=hadoop.log -Dyarn.home.dir=/home/LIN/software/hadoop-3.0.3 -Dyarn.root.logger=INFO,console -Djava.library.path=/home/LIN/software/hadoop-3.0.3/lib/native -Dhadoop.log.dir=/home/LIN/software/hadoop-3.0.3/logs -Dhadoop.log.file=hadoop.log -Dhadoop.home.dir=/home/LIN/software/hadoop-3.0.3 -Dhadoop.id.str=LIN -Dhadoop.root.logger=INFO,console -Dhadoop.policy.file=hadoop-policy.xml -Dhadoop.security.logger=INFO,NullAppender
org.apache.hadoop.util.RunJar
share/hadoop/mapreduce/hadoop-mapreduce-examples-3.0.3.jar grep input output dfs[a-z.]+
[LIN@localhost hadoop-3.0.3]$
下面我们将开始分析org.apache.hadoop.util.RunJar启动类
2.mapreduce任务启动流程
RunJar启动类在hadoop-3.0.3-src/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util 目录下可以找到,我们重点定位到main函数以及run方法
public static void main(String[] args) throws Throwable {
new RunJar().run(args);
}
public void run(String[] args) throws Throwable {
String usage = "RunJar jarFile [mainClass] args...";
if (args.length < 1) {
System.err