hadoop作业引用第三方jar文件

来源:互联网 发布:微店和淘宝哪个安全 编辑:程序博客网 时间:2024/05/01 17:47
 编写mapreduce程序,习惯用eclipse,可以利用hadoop插件,引用第三方jar文件,然后直接run on hadoop即可,很方便。当然插件和eclipse的版本要匹配,不然总是local执行。但如果将自己的程序发布成jar文件,然后用hadoop命令行执行,则会遇到依赖类找不到问题:NoClassDefFoundError
    要解决这个问题,就需要了解hadoop命令式如何执行的?$HADOOP_HOME/bin/hadoop是一个脚本文件。Hadoop作业提交分析中分析了这个脚本,脚本最终执行的是
[plain] view plaincopy
  1. <span style="font-size:16px;">exec "$JAVA" $JAVA_HEAP_MAX $HADOOP_OPTS -classpath "$CLASSPATH" $CLASS "$@"</span>  
    对于用hadoop命令执行jar来说,其$CLASS是类org.apache.hadoop.util.RunJar,而$CLASSPATH初始赋值为"${HADOOP_CONF_DIR}",然后加入$HADOOP_HOME等的各种jar文件,最后加入自定义的$HADOOP_CLASSPATH,$@是命令传入参数。但目前的配置只是为了运行RunJar,这个类的功能比较简单,将jar文件解压到“hadoop.tmp.dir”目录下,然后执行我们指定的类,如命令行
[plain] view plaincopy
  1. hadoop jar example.jar wordcount input output  
RunJar解压example.jar,执行wordcount,将参数传给main方法。接下来就是我们写的程序的,通过调用job.waitForCompletion(true)来执行job。本以为将第三方jar文件加入到$HADOOP_CLASSPATH就可以了,谁知当真正执行作业时还是会出错,因为对于job程序,如wordcount,它无法找到这些jar文件。
现在来分析下job的提交过程,先看图

  1. run job: 通过Job类实际上调用了JobClient的submitJobInternal提交作业
  2. get new job ID: JobClient通过JobSubmissionProtocol的实例来获得一个作业号,这个实例的初始化如下所示
    [java] view plaincopy
    1. <span style="font-size:16px;">String tracker = conf.get("mapred.job.tracker""local");  
    2. if ("local".equals(tracker)) {  
    3.   this.jobSubmitClient = new LocalJobRunner(conf);  
    4. else {  
    5.   this.jobSubmitClient = createRPCProxy(JobTracker.getAddress(conf), conf);  
    6. }</span>  
    由configuration配置文件决定作业时提交给LocalJobRunner还是NameNode上的JobTracker。
  3. copy job resource: client将作业所需资源上传到hdfs上,如job split、jar文件等。其中jar文件时我们主要分析的,JobClient通过configureCommandLineOptions函数处理jar文件,顾名思义,该方法是处理命令行的参数。方法中通过job获得这些参数内容
    [java] view plaincopy
    1. <span style="font-size:16px;">files = job.get("tmpfiles");  
    2. libjars = job.get("tmpjars");  
    3. archives = job.get("tmparchives");</span>  
    分别对应参数想-files, -libjars以及-archives。若有配置,则程序将其加入到分布式缓存DistributedCache中。
    [java] view plaincopy
    1. <span style="font-size:16px;">if (files != null) {  
    2.   FileSystem.mkdirs(fs, filesDir, mapredSysPerms);  
    3.   String[] fileArr = files.split(",");  
    4.   for (String tmpFile: fileArr) {  
    5.     Path tmp = new Path(tmpFile);  
    6.     Path newPath = copyRemoteFiles(fs,filesDir, tmp, job, replication);  
    7.     try {  
    8.       URI pathURI = new URI(newPath.toUri().toString() + "#" + newPath.getName());  
    9.       DistributedCache.addCacheFile(pathURI, job);  
    10.     } catch(URISyntaxException ue) {  
    11.       //should not throw a uri exception   
    12.       throw new IOException("Failed to create uri for " + tmpFile);  
    13.     }  
    14.     DistributedCache.createSymlink(job);  
    15.   }  
    16. }  
    17.   
    18. if (libjars != null) {  
    19.   FileSystem.mkdirs(fs, libjarsDir, mapredSysPerms);  
    20.   String[] libjarsArr = libjars.split(",");  
    21.   for (String tmpjars: libjarsArr) {  
    22.     Path tmp = new Path(tmpjars);  
    23.     Path newPath = copyRemoteFiles(fs, libjarsDir, tmp, job, replication);  
    24.     DistributedCache.addArchiveToClassPath(newPath, job);  
    25.   }  
    26. }  
    27.   
    28.   
    29. if (archives != null) {  
    30.  FileSystem.mkdirs(fs, archivesDir, mapredSysPerms);   
    31.  String[] archivesArr = archives.split(",");  
    32.  for (String tmpArchives: archivesArr) {  
    33.    Path tmp = new Path(tmpArchives);  
    34.    Path newPath = copyRemoteFiles(fs, archivesDir, tmp, job, replication);  
    35.    try {  
    36.      URI pathURI = new URI(newPath.toUri().toString() + "#" + newPath.getName());  
    37.      DistributedCache.addCacheArchive(pathURI, job);  
    38.    } catch(URISyntaxException ue) {  
    39.      //should not throw an uri excpetion  
    40.      throw new IOException("Failed to create uri for " + tmpArchives);  
    41.    }  
    42.    DistributedCache.createSymlink(job);  
    43.  }  
    44. }</span>  
    我们的mapreduce程序中,总会通过job.setJarByClass方法指定要我们自己的类,由此JobClient通过job.getJar()得到该jar文件,并将其copy到指定目录中(setJarByClass这里就不细讲了)。到此jobClient完成了资源复制过程,这些资源可供JobTracker和TaskTracker使用
  4. JobClient提交job,JobTracker以及TaskTracker工作就不展开了。
    经过对job提交过程的分析可知,要想让mapreduce程序引用第三方jar文件,可以采用如下方式
  1. 通过命令行参数传递jar文件,如-libjars等;
  2. 直接在conf中设置,如conf.set(“tmpjars”,*.jar),jar文件用逗号隔开;
  3. 利用分布式缓存,如DistributedCache.addArchiveToClassPath(path, job),此处的path必须是hdfs,即自己讲jar上传到hdfs上,然后将路径加入到分布式缓存中,本质是一样的;
  4. 第三方jar文件和自己的程序打包到一个jar文件中,程序通过job.getJar()将获得整个文件并将其传至hdfs上。
    不过从开发角度来看,还是eclipse插件比较方便,或者采用Hadoop作业提交分析的方法,程序员好懒!
0 0
原创粉丝点击