Hadoop开发中必然会大量使用第三方的包,如Mahout、Hive等。在运行时,一个通用的做法是将第三方的包全部打包到当前项目中,和项目编译产生的classes文件一起,构建成一个巨大的jar包,然后直接运行这个包即可。
一般在eclipse中,可以使用ant来做打包工作,类似的ant文件如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="create_run_jar">
<!--this file was created by Eclipse Runnable JAR Export Wizard-->
<!--ANT 1.7 is required -->
<target>
<copyfile dest="D:/hadoop/conf/config.mix.xml" src="https://www.linuxidc.com/D:/projects/up/recomm/src/config.mix.xml"/>
<jar destfile="D:/hadoop/work/reckeyword.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute value="com.sumsung.profile.analyze.keyword.mix.RecommenderDriver"/>
<attribute value="."/>
</manifest>
<!--fileset dir="D:/projects/up/.metadata/.plugins/org.apache.hadoop.eclipse/hadoop-conf-5736823963924267363"/-->
<zipfileset excludes="META-INFlicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicenselicense/**" src="https://www.linuxidc.com/D:/projects/up/recomm/mahout/xpp3_min-1.1.4c.jar"/>
<fileset dir="D:/projects/up/recomm/classes"/>
</jar>
</target>
</project>
这样可以将所有jar包都打包到目标jar中,在hadoop中直接运行这个jar包即可。
如果hadoop是在本地或者局域网里面,那这么做是没问题的,但是如果hadoop是在远程,比如现在很多公司使用amazon提供的mapreduce,将mahout所有相关的类打包,形成最终的jar,至少在25兆以上,国内网速这么慢,每次调试都要耗费大量的时间。
解决方法是使用hadoop的libjar参数,通过这个参数来引入第三方的包,但是关于这个参数,在所有文档中说明都很简略。估计用的人也不多。
在hadoop中对这个参数解释:这是一个通用的参数,可以用来指定项目运行需要的第三方类库,不同的jar可以使用逗号分隔。(-libjars <comma seperated list of jars> Specify comma separated jar files to include in the classpath. )
如果在参数中使用-libjars来指定第三方类库,那hadoop会在什么时候加载这个类库?会放在哪个classloader中呢?这个需要参考到源代码,和libjars相关的代码。
首先,如果要使这个参数生效,必须使用ToolRunner来运行Job。因为相关代码是通过ToolRunner来调用GenericOptionsParser来实现的。
看一下GenericOptionsParser:
private void processGeneralOptions(Configuration conf,
CommandLine line) {
......
try {
if (line.hasOption("libjars")) {
conf.set("tmpjars",
validateFiles(line.getOptionValue("libjars"), conf));
//setting libjars in client classpath
URL[] libjars = getLibJars(conf);
if(libjars!=null && libjars.length>0) {
conf.setClassLoader(new URLClassLoader(libjars, conf.getClassLoader()));
Thread.currentThread().setContextClassLoader(
new URLClassLoader(libjars,
Thread.currentThread().getContextClassLoader()));
}
}
.....
}