|
1.6 编译及调试Hadoop源代码
6 `5 V! s: k* U* Z: _* k读者在阅读源代码过程中, 可能需要修改部分源代码或者使用调试工具以便跟踪某些变量值变化过程, 此时要用到Hadoop3 j; |% o$ j; Q8 G
源代码编译和调试方法。 本节将介绍Hadoop在伪分布式模式下的编译和调试方法, 其中调试方法主要介绍使用Eclipse远程调试工; c1 I% h/ U K. \5 w' E! B, S
具和打印调试日志两种。+ Y, d0 M; L2 r6 J/ E' F5 K
Hadoop天生支持Linux而对其他操作系统( 如Windows) 很不友好, 本书也鼓励读者直接在Linux平台下编译和调试Hadoop源代
- L$ L' s$ `/ d! z码, 因此, 本节介绍的内容全部在Linux环境下。; ?/ D4 t9 g. L6 P4 K4 n
1.6.1 编译Hadoop源代码9 p$ j* H- l. F1 d6 h
在Linux环境下编译源代码之前, 需进行以下准备工作:
& c- d0 U& W9 Y! q$ ^❑确保安装的Maven版本在3.0.2以上;" z+ n! a3 ~5 _1 ?% g8 q7 L9 D5 F
❑Protocol Buffers安装版本为2.5.0;
6 z% t4 }7 q- e3 Q8 N: v❑如果要启用findbugs, 则需确认已经安装了Findbugs;
) G8 `* [7 @( ~" b% ?3 r8 d# X❑如果要编译native code, 则需确认安装了CMake 2.6或者更新版本;2 J2 r, H4 v+ e
❑第一次编译代码, 需确认可以连接互联网( Maven要从代码库中下载依赖包) 。
! D& t8 `' l5 ~: A+ y1 n# GMaven编译命令如表1-2所示。
9 _9 ? ~! J# I8 q. B表1-2 Maven编译命令
2 q6 m/ X3 \9 o! J; P6 [" ^; y3 D如果仅编译生成JAR包而无须编译native code、 测试用例和生成文档, 可在Hadoop安装目录下并输入以下命令( 推荐使用该
8 A& u9 E% g t6 E* i% Q* k命令编译Hadoop源代码) :8 l1 c4 b/ j' Y6 ]8 E- D* @1 t& r
mvn package -Pdist -DskipTests -Dtar
. Q; W& A. V3 d. {3 {( `如果编译JAR包、 native code并生成文档, 可使用以下命令:
0 N1 e8 ~* d2 U* y7 }9 N$ mmvn package -Pdist,native,docs -DskipTests -Dtar
% ?) _ C6 P y' _* N! v* `2 d每个子模块编译后生成的JAR包放到了与源代码目录平级的target目录中, 比如ResourceManager的源代码目录是:
- I9 J6 a% [7 D9 H6 {% C${YARN_HOME}/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-
2 \3 J: C8 O" x9 aserver/hadoop-yarnserver-resourcemanager/src7 u! _8 _) v8 w/ i8 H8 Z
它对应生成JAR包放在了以下目录中:
/ V* u3 v) t; F1 p5 p3 h0 u! B${YARN_HOME}/hadoop-yarn-project/hadoop-yarn/hadoop-yarnserver/hadoop-yarnserver-resourcemanager/target
3 O1 y/ R3 h7 Y e& \; |" k. {; z如果修改了某个模块的代码, 可编译后, 将对应的JAR包覆盖到${HADOOP_HOME}/share/hadoop目录中对应的JAR包上。% ?0 l9 c& w a" S7 x- j
如果仅编译Hadoop的某一个子模块, 需将该模块依赖的JAR包作为它的第三方库引入。 一种简单的实现方式是在Hadoop安装0 ^' p( T& Q/ F. }+ k' D; ~
目录下输入以下命令编译所有源代码:
! `: H3 M/ J$ `0 t9 D X, _* I$ Fmvn install -DskipTests, w [+ R3 j \2 o
然后进入子模块目录, 编译生成对应的JAR包。. W4 k6 a2 S! {
1.6.2 调试Hadoop源代码
% |3 D k9 S4 c4 P- |% c本节介绍两种调试Hadoop源代码的方法: 利用Eclipse远程调试工具和打印调试日志。 这两种方法均可以调试伪分布式工作模) j6 _4 x- u9 \$ U8 K
式和完全分布式工作模式下的Hadoop。 本节主要介绍伪分布式工作模式下的Hadoop调试方法。( I5 d& ?5 ] \) c* y0 a0 K$ p8 n
( 1) 利用Eclipse进行远程调试; ]5 B. q. O% ~
下面以调试ResourceManager为例, 介绍利用Eclipse远程调试的基本方法, 这可分两步进行。
0 [# a4 I, I5 U+ ?, h步骤1 调试模式下启动Hadoop。& C% v2 i6 p; j) q/ b1 v+ x
在Hadoop安装目录下运行如下的Shell脚本:. z3 G( N# N' ]2 q* S$ C& A
export YARN_NODEMANAGER_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8788,
" Z# F: n/ ~& g, Mserver=y,suspend=y"$ o0 j. C: M" k% I4 q' x) W
sbin/start-all.sh
0 f6 e& W `( |% o- P2 \运行了脚本后会看到Shell命令行终端显示如下信息:. H: s+ i- Z; S: P4 I1 Y
Listening for transport dt_socket at address: 8788
0 D' d- Q6 R" t/ J: z) B" M8 X8 `1 p k此时表明ResourceManager处于监听状态, 直到收到debug确认信息。 Y, p# S1 @. S" Z! l% `
步骤2 设置断点。9 z. U5 t& S1 m0 {
在前面新建的Java工程"hadoop-2.0"中, 找到ResourceManager相关代码, 并在感兴趣的地方设置一些断点。& |2 [ m2 j- k9 ^: @9 ~/ a
步骤3 在Eclipse中调试Hadoop程序。
& r" f" s a/ g在Eclipse的菜单栏中, 依次选择"Run"→"Debug Configurations"→"Remote Java Applications", 并按照要求填写远程调试器名称
. T% _( y* m5 b! l. V( 自己定义一个即可) , ResourceManager所在host以及监听端口号等信息, 并选择Hadoop源代码工程, 便可进入调试模式。0 ?" m, T! k* k0 J
调试过程中, ResourceManager输出的信息被存储到日志文件夹下的yarn-XXX-resourcemanager-localhost.log文件( XXX为当前用 f( h/ Q" E- T8 C4 c0 E
户名) 中, 可通过以下命令查看调试过程中打印的日志:
4 w4 s* @) b( m6 ]5 x; M, O) f& Rtail -f logs/yarn-XXX-resourcemanager-localhost.log
; W6 Q- b/ r. p( 2) 打印Hadoop调试日志
( g( d6 L# w0 a: K' I; Z7 eHadoop使用了 Apache log4j [5] 作为基本日志库, 该日志库将日志分别5个级别, 分别为DEBUG、 INFO、 WARN、 ERROR和5 ]: _' _0 [8 U* j8 j" X
FATAL。 这5个级别是有顺序的, 即DEBUG <INFO <WARN <ERROR <FATAL, 分别用来指定日志信息的重要程度。 日志输出规
# X1 S2 \" t7 [; {1 ]/ ]: q- Y4 z则为: 只输出级别不低于设定级别的日志信息, 比如若级别设定为INFO, 则INFO、 WARN、 ERROR和FATAL级别的日志信息都8 @0 K1 u; m+ ]
会输出, 但级别比INFO低的DEBUG则不会输出。0 O! u, d0 \& N) C
在Hadoop源代码中, 大部分Java文件中存在调试日志( DEBUG级别日志) , 但默认情况下, 日志级别是INFO, 为了查看更
2 G5 N) ?% b" k `4 S2 a4 V详细的运行状态, 可采用以下几种方法打开DEBUG日志。
% W% J! `2 \# j方法1 使用Hadoop Shell命令。4 `# ~% q2 V4 D& r) H; V
可使用Hadoop脚本中的daemonlog命令查看和修改某个类的日志级别, 比如, 可通过以下命令查看NodeManager类的日志级3 Z) U+ Q1 s% ?
别:
+ G% |- b* E- e7 F, Ybin/hadoop daemonlog -getlevel ${nodemanager-host}:8042 \
0 |# _: N, f5 i) y1 @9 e8 ^org.apache.hadoop.yarn.server.nodemanager.NodeManager9 @1 F$ g- k$ d1 j4 v
可通过以下命令将NodeManager类的日志级别修改为DEBUG:
. R5 l# `* `( D0 Y% p' `. rbin/hadoop daemonlog -setlevel ${nodemanager-host}:8042 \0 {% @- A( b4 G
org.apache.hadoop.yarn.server.nodemanager.NodeManager DEBUG
. t5 l# P2 B6 ?% R其中, nodemanager-host为NodeManager服务所在的host, 8042是NodeManager的HTTP端口号。
/ J) V; r% o9 m1 z方法2 通过Web界面。! B/ j" H+ T4 ?9 W2 ?' J1 }1 ~
用户可以通过Web界面查看和修改某个类的日志级别, 比如, 可通过以下URL修改NodeManager类的日志级别:
# I) i# N7 M6 M- u/ d1 @8 rhttp://${nodemanager-host}:8042/logLevel
5 N( y3 i, b8 B; }: ]2 |$ ]3 a方法3 修改log4j.properties文件。$ l* j4 ^" O1 ^+ P1 g' u
以上两种方式只能暂时修改日志级别, 当Hadoop重启后会被重置, 如果要永久性改变日志级别, 可在目标节点配置目录下' i$ y0 `! f; F) I! j5 b
的log4j.properties文件中添加以下配置选项:& c A9 E( d2 @/ S6 a a0 P& m
log4j.logger.org.apache.hadoop.yarn.server.nodemanager.NodeManager=DEBUG1 A# k5 s) m/ I1 o
此外, 有时为了专门调试某个Java文件, 需要把该文件的相关日志输出到一个单独文件中, 可在log4j.properties中添加以下内# D8 O7 F& Y/ j% w) b
容:: _4 ^! x E& ?. w+ i8 n
# 定义输出方式为自定义的TTOUT) D& ?5 S7 K1 v- M6 |* ^' V
log4j.logger. org.apache.hadoop.yarn.server.nodemanager.NodeManager=DEBUG,TTOUT k2 o) r' t" e/ W" P' Q1 H
# 设置TTOUT 的输出方式为输出到文件
' Y6 q5 S) h' mlog4j.appender.TTOUT =org.apache.log4j.FileAppender
' S9 {+ K5 {9 f5 ~9 R2 a! v# 设置文件路径0 Y+ i) W1 R" k0 A0 c6 i
log4j.appender.TTOUT.File=${hadoop.log.dir}/NodeManager.log, s, m3 |8 _/ f! ~$ I3 r
# 设置文件的布局/ ^, J2 Z. R! l" j2 J
log4j.appender.TTOUT.layout=org.apache.log4j.PatternLayout% q- h3 C$ h$ v3 p, Y
# 设置文件的格式
5 i1 I1 J1 y2 }0 b/ ?log4j.appender.TTOUT.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
7 {6 f @% { l7 p8 A7 ]这些配置选项会把NodeManager.java中的DEBUG日志写到日志目录下的NodeManager.log文件中。
; G2 ?9 m5 n/ [# }4 Y在阅读源代码的过程中, 为了跟踪某个变量值的变化, 读者可能需要自己添加一些DEBUG日志。 在Hadoop源代码中, 大部4 t" ]( I5 [# i6 q0 R5 z h
分类会定义一个日志打印对象, 通过该对象可打印各个级别的日志。 比如, 在NodeManager中用以下代码定义对象LOG:& U/ Z# z7 L: ^5 i; F
public static final Log LOG = LogFactory.getLog(NodeManager.class);
, {; |: I, F; R用户可使用LOG对象打印调试日志。 比如, 可在NodeManager的main函数首行添加以下代码:+ w! ?# Z) N, U3 Z' ^& b7 u- m
LOG.debug("Start to lauch NodeManager...");
* E! ?1 Y8 e Y+ K然后重新编译Hadoop源代码, 并将org.apache.hadoop.yarn.server.nodemanager.NodeManager的调试级别修改为DEBUG, 重新启8 e; [% {. h/ c9 e
动Hadoop后便可以看到该调试信息。! C% I; o( }8 m& j: y
[5] Apache log4j网址: http://logging.apache.org/log4j/index.html。
$ e+ ]: V; r! S: A% ~8 S$ [1.7 小结* N% [) X1 ?/ _, g3 g
搭建一个高效的源代码学习环境是深入学习Hadoop的良好开端, 本章主要内容正是帮助读者搭建一个这样的学习环境。 在
$ B0 ]6 e: l, e) x0 P+ }笔者看来, 一个高效的Hadoop学习环境至少应该包括源代码阅读环境、 Hadoop使用环境和源代码编译调试环境, 而本章正是围
: r+ n; ?! Y q绕这三个环境的搭建方法组织的。1 W1 t5 q. j; C" b: O
本章介绍了Linux环境下搭建Hadoop源代码阅读环境的方法, 在此基础上, 进一步介绍了Hadoop的基本使用方法, 主要涉及
8 n7 V0 a. h. ZHadoop Shell和Eclipse插件两种工具的使用。 最后介绍了Hadoop源代码编译和调试方法, 其中, 调试方法主要介绍了使用Eclipse远1 z" e6 j( g: r: @/ w! |, ?
程调试和打印调试日志两种。
6 e2 v: ~5 `+ a% |! N1 }2 l9 r* N: H
% m/ ^# I, w) n" _" [
|
|