hadoop程序中使用第三方jar包遇到的java.lang.NoSuchMethodException

在写hadoop程序时,经常遇到这种找不到类的定义的异常 java.lang.RuntimeException: java.lang.NoSuchMethodException

(1)第一种是在提交任务的时候要用到第三方jar包,比如job提交之前要先解析xml文件。

需要在脚本中export jar包的路径,这样java虚拟机才知道从哪里去加载jar包, 类似于java -classpath参数。

export HADOOP_CLASSPATH=$JAR_DIR/dom4j-1.6.1.jar:$JAR_DIR/jaxen-1.1-beta-6.jar

(2)第二种是在程序运行的过程中用到第三方jar包。由于是分布式程序,jar包需要每台运行任务的机器上都要有。

hadoop提供了ToolRunner类来自动将jar包上传到hdfs上,然后tasktracker机器会从hdfs get到本地目录

具体用法是:

import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

public class TestJob extends Configured implements Tool {

    static int printUsage() {
        System.out.println("Join -f conf_name -r reduce_num");
        ToolRunner.printGenericCommandUsage(System.out);
        return -1;
    }

    @Override
    public int run(String[] args) throws Exception {

        Configuration conf = getConf();
        Job job = new Job(conf, "TestJob");
        Configuration configure = job.getConfiguration();

        ......

        job.setNumReduceTasks(Integer.parseInt(reduce_num));

        job.setJarByClass(JoinMap.class);
        job.setJarByClass(JoinReduce.class);

        job.setMapperClass(JoinMap.class);
        job.setReducerClass(JoinReduce.class);

        return (job.waitForCompletion(true) ? 0 : 1);
    }

    public static void main(String[] args) throws Exception {
        int ret = ToolRunner.run(new Configuration(), new TestJob(), args);

        System.exit(ret);
    }
}

在提交job的时候用 -libjars 来指定用到的第三方jar就可以了

hadoop jar TestJob.jar com.etao.dump.TestJob -f full-conf.xml -libjars /tmp/dom4j-1.6.1.jar,/tmp/jaxen-1.1-beta-6.jar

通过查看ToolRunner的源代码可以指定,它是用GenericOptionsParser来解析参数的,将指定的jar包放在conf文件的tmpjars参数中,然后创建classloader。这样,我们也可以通过conf.set(“tmpjars”,”/tmp/dom4j.jar”)或者修改conf文件,来指定用到的jar包。

 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()));
      }
    }

(3) 此外,还有一种java.lang.NoSuchMethodException异常比较隐蔽,和java的反射有关。

如果在程序中自定义了一个类,比如LongIntPair,定义如下:

/**
 * Define a pair of long ant int that are writable. They are serialized in a
 * byte comparable format.
 */
public class LongIntPair implements WritableComparable<LongIntPair> {
    long first;
    int second;

    public LongIntPair(long uid, int i) {
        first = uid;
        second = i;
    }

    public long getFirst() {
        return first;
    }

    public int getSecond() {
        return second;
    }

    .....
}

然后在程序中使用了这个类,运行的时候会报如下的异常:

java.lang.RuntimeException: java.lang.NoSuchMethodException: com.etao.dump.LongIntPair.<init>()
    at org.apache.hadoop.util.ReflectionUtils.newInstance(ReflectionUtils.java:115)
    at org.apache.hadoop.io.WritableComparator.newKey(WritableComparator.java:109)
    at org.apache.hadoop.io.WritableComparator.<init>(WritableComparator.java:95)
    at org.apache.hadoop.io.WritableComparator.get(WritableComparator.java:51)
    at org.apache.hadoop.mapred.JobConf.getOutputKeyComparator(JobConf.java:795)
    at org.apache.hadoop.mapred.MapTask$MapOutputBuffer.<init>(MapTask.java:817)
    at org.apache.hadoop.mapred.MapTask$NewOutputCollector.<init>(MapTask.java:557)
    at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:639)
    at org.apache.hadoop.mapred.MapTask.run(MapTask.java:323)
    at org.apache.hadoop.mapred.Child$4.run(Child.java:270)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:396)
    at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1157)
    at org.apache.hadoop.mapred.Child.main(Child.java:264)
Caused by: java.lang.NoSuchMethodException: com.etao.lbs.jobHelper.LongIntPair.<init>()
    at java.lang.Class.getConstructor0(Class.java:2706)
    at java.lang.Class.getDeclaredConstructor(Class.java:1985)
    at org.apache.hadoop.util.ReflectionUtils.newInstance(ReflectionUtils.java:109)
    ... 13 more
2012-06-03 14:30:52,783 INFO org.apache.hadoop.mapred.Task: Runnning cleanup for the task

原因是java的反射机制要求类必须要有无参构造方法。当一个类没有构造方法时, java会缺省为其加一个无参构造方法。

但是由于为LongIntPair类添加了一个有参构造函数,java就不会再缺省添加无参构造函数了,这样这个类就不支持反射。

发表评论

电子邮件地址不会被公开。 必填项已用*标注