搭建了一个Hadoop集群(一个master,三个slave),参照Hadoop实战上的wordcount代码,在eclipse运行,但是在web页面上查不到该job的id,后来发现是在单机上运行,那搭建这个集群还有什么意义,于是在网上查找答案,试着在eclipse中将hadoop程序在集群中跑起来。却发现其中问题不少啊。
1.如何使eclipse中的hadoop程序在集群而不是单机上运行
通过源码,首先是创建一个configuration对象,并且对参数进行解析,接着提交作业所用的Job对象,设置作业jar包,对应的Mapper类和Reducer类,输入输出的Key和Value的类及作业的输入和输出路径,最后就是提交作业并等待作业结束。
String tracker = conf.get("mapred.job.tracker", "local");
if ("local".equals(tracker)) {
this.jobSubmitClient = new LocalJobRunner(conf);
} else {
this.jobSubmitClient = createRPCProxy(JobTracker.getAddress(conf), conf);
}
发现原来跟conf文件夹中mapred-site.xml中mapred.job.tracker的属性是否设置有关,在eclipse之前都没将配置文件添加进来,怪不得在Local运行,后来想想也是,你的配置文件都没添加进来,都找不到JobTracker和TaskTracker的位置,怎么可能在集群运行呢······解决这个问题很简单,只需要将hadoop中已经配置好的conf文件夹考到当前工程,并添加到工程的build path中,这样就能在集群上运行了!(这同时也解释了在命令行为什么就不是在单机上运行了,因为conf中的配置文件是已经添加到路径中去了的)
具体可以参考博客http://www.cnblogs.com/spork/archive/2010/04/21/1717552.html 这位老兄讲得很详细。
2.Map函数无法找到
做完第一步后,以为可以运行了,却发现提示java.lang.RuntimeException: java.lang.ClassNotFoundException: Job$Mapper.
看了下编译后的文件,发现Map和Reduce内部类已经编译好了啊,为什么会找不到啊,果断把提示的错误复制,百度,发现是Job.setJarByClass(xxx.class)设置jar没有成功,将网上答案抄了下来
在eclipse运行,使用了WordCount.class的类加载器来寻找包含该类的Jar包,然后设置该Jar包为作业所用的Jar包。但是我们的作业 Jar包是在程序运行时才打包的,而WordCount.class的类加载器是AppClassLoader,运行后我们无法改变它的搜索路径,所以使用setJarByClass是无法设置作业Jar包的。我们必须使用JobConf里的setJar来直接设置作业Jar包。就是添加下面这一句
((JobConf)job.getConfiguration()).setJar(jarFile);
其中的jarFile是你已经打包好了的,输入该jar包的路径就OK(在命令行中完全省略这一步,直接job.setJarByClass就好了)
参考博客http://www.cnblogs.com/spork/archive/2010/04/21/1717592.html
3.如何打包
上文提到,需要自己打包,当你的代码中没有用到第三方的jar包的时候,这时是相当轻松的,使用eclipse的export,无论打成普通的jar包还是runnable jar都是没有问题的。但是当时我的应用中是用到了第三方的jar包的,由于hadoop需要将该jar包分发到tasktracker运行,而tasktracker主机肯定是没有下载第三方jar包的。于是我想到了打成一个runnable jar,里面包含我所需要的第三方jar包。我使用该jar包中的类是在Map函数中,Map函数肯定是在Tasktracker运行,但是运行时提示始终找不到该类,我明明是加进去了的啊,我猜想是不是打包格式不对啊,烦!百度之,找关于hadoop如何分发本机jar包,果不其然,两种解决方案
1.将第三方的jar包所有的class与我们自己代码打包在一起。这其实很容易做到,使用eclipse的export功能打包成runnable jar,然后选第一项就可以了。
2.创建一个lib目录,把所依赖的jar包放到该目录,再和代码一起打包。
参考博客http://blog.iamzsx.me/show.html?id=185002
4.在命令行如何运行有层次的jar包
比如我的主类是Wordcount,但是该类是在包word下,那么运行在eclipse底下是没任何问题的,但是当打包以后,却发现不能运行。网上找答案,发现无一例外都是要运行下面的命令Hadoop jar xxx.jar word.Wordcount input output
但是还是不行,最后发现当打包runnable的第一种方式,即第三方的jar包所有的class与我们自己代码打包在一起,并设置好主类就可以了(在MAINFEST中进行设置)。 这时候的命令为 Hadoop jar xxx.jar input output(不需要设置主函数,因为在打包时候已经设置好了)
总结下,上面几个看似很简单的问题花费了我几天时间,但想想问题出现在:
1.对打包方式不熟悉
2.没有深刻理解hadoop的内部机制
才会出现上述问题
另外,分享打包语句
jar cvfm xxx.jar manifest -C 文件夹/ . -m 是用自己的mainfest(Main-Class)