共计 2608 个字符,预计需要花费 7 分钟才能阅读完成。
在 eclipse 中写 mapreduce 程序, 引用第三方 jar 文件, 可以利用 eclipse Hadoop 插件直接 run on hadoop 提交, 很方便. 不过插件版本要和 eclipse 匹配, 不然总是 local 执行, 在 50070 是没有 job 产生的.
如果希望将程序发布成 jar 文件, 在 namenode 上通过命令行方式执行, 缺少了 eclipse 帮忙自动配置 jar 文件, 会遇到 java.lang.ClassNotFoundException, 这个问题可分成两种情况讨论.
一. hadoop 命令式如何执行的?
其实 $HADOOP_HOME/bin/hadoop 是一个脚本文件. 以下 wordcount 命令为例
bin/hadoop jar wordcount.jar myorg.WordCount /usr/wordcount/input /usr/wordcount/output
脚本文件解析参数, 配置类路径等, 最终执行的是如下命令:
exec java -classpath $CLASSPATH org.apache.hadoop.util.RunJar $@
其中 $CLASSPATH : 包含${HADOOP_CONF_DIR}, $HADOOP_HOME 下的 *.jar 以及$HADOOP_CLASSPATH;
- $@ : 所有脚本参数, 此处为 jar 后面的参数;
- RunJar : 这个类的功能比较简单, 将 jar 文件解压到 “hadoop.tmp.dir” 目录下, 然后执行我们指定的类, 此处即为myorg.WordCount
p.s. hadoop 脚本比较完整的分析可参见 <Hadoop 作业提交分析 http://www.linuxidc.com/Linux/2012-04/59199.htm >.
有 RunJar 执行 WordCount 后, 就进入我们的程序了, 需要配置 mapper, reducer 以及输出输出路径等等, 最终通过执行 job.waitForCompletion(true)向 JobTracker 提交这个作业.
到目前可知, 已经完成了本地执行部分, 如果这段时期发生 ClassNotFoundException, 则可以在自己的脚本文件中配置 $HADOOP_CLASSPATH, 包含需要的第三方 jar 文件, 再执行 hadoop 命令, 此为情况一.
二. JobTracker 和 TaskTracker 如何获得第三方 jar 文件?
有时候提交 job 之后, 在 map 或者 reduce 函数中也会产生 ClassNotFoundException. 这是因为 map 或 reduce 可能在其他机器上执行, 那些机器没有需要的 jar 文件, mapreduce 作业交由 JobTracker 和 TaskTracker 执行, 两者如何获得第三方 jar 文件呢? 即为情况二.
我们首先来分析下 mapreduce 提交过程, 如下图所示.
step 1. 和 2. 通过 Job 类提交作业, 获得一个作业号, 并根据 conf 决定作业时提交给 LocalJobRunner 还是 JobTracker
step 3. copy job resource
client 将作业所需资源上传到 hdfs 上, 如 job split, jar 文件等. JobClient 通过 configureCommandLineOptions 函数处理 jar 文件, 该方法中通过 job 获得这些参数内容
files = job.get("tmpfiles"); // 对应参数项 -files
libjars = job.get("tmpjars"); // 对应 -libjars
archives = job.get("tmparchives"); // 对应 -archives
如果 jar 文件有配置, 则将其加入到分布式缓存 DistributedCache 中, -libjars 为例:
if (libjars != null) {FileSystem.mkdirs(fs, libjarsDir, mapredSysPerms);
String[] libjarsArr = libjars.split(",");
for (String tmpjars: libjarsArr) {Path tmp = new Path(tmpjars);
Path newPath = copyRemoteFiles(fs, libjarsDir, tmp, job, replication);
DistributedCache.addArchiveToClassPath(newPath, job);
}
}
另外, 在 mapreduce 程序的配置中总是需要 job.setJarByClass 来指定运行的类, 如此 hadoop 就可以根据该 class 定位到所在的 jar 文件, 就是我们打包的 jar, 将其上传到 hdfs 上. 到此 jobClient 完成了资源复制过程, 这些资源可供 JobTracker 和 TaskTracker 使用.
step4-10. JobClient 提交 job 并执行作业(JobTracker 以及 TaskTracker 工作就不展开了, 详见 <Map-Reduce 过程解析> http://www.linuxidc.com/Linux/2011-11/47052.htm).
三. 总结
- 通过命令行参数传递 jar 文件, 如 -libjars 等;
- 直接在 conf 中设置, 如 conf.set(“tmpjars”,*.jar), jar 文件用逗号隔开;
- 利用分布式缓存, 如 DistributedCache.addArchiveToClassPath(path, job), 此处的 path 必须是 hdfs, 即自己讲 jar 上传到 hdfs 上, 然后将路径加入到分布式缓存中;
- 第三方 jar 文件和自己的程序打包到一个 jar 文件中, 程序通过 job.getJar()将获得整个文件并将其传至 hdfs 上. (很笨重)
- 在每台机器的 $HADOOP_HOME/lib 目录中加入 jar 文件. (不推荐)
p.s. 如果通过上面方法 1. 或 2., 需要注意 Configuration 问题, 需要通过 getConf()函数获得, 而不要自己 new 一个对象.
Hadoop 怎样提交多个第三方 jar 包?http://www.linuxidc.com/Linux/2012-02/53759.htm
更多 Hadoop 相关信息见Hadoop 专题页面 http://www.linuxidc.com/topicnews.aspx?tid=13