【问题标题】:hadoop MultipleInputs fails with ClassCastExceptionhadoop MultipleInputs 失败并出现 ClassCastException
【发布时间】:2012-06-23 05:09:07
【问题描述】:

我的 hadoop 版本是 1.0.3,当我使用多个输入时,我得到了这个错误。

java.lang.ClassCastException: org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit cannot be cast to org.apache.hadoop.mapreduce.lib.input.FileSplit
at org.myorg.textimage$ImageMapper.setup(textimage.java:80)
at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:142)
at org.apache.hadoop.mapreduce.lib.input.DelegatingMapper.run(DelegatingMapper.java:55)
at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:764)
at org.apache.hadoop.mapred.MapTask.run(MapTask.java:370)
at org.apache.hadoop.mapred.Child$4.run(Child.java:255)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:416)
at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1121)
at org.apache.hadoop.mapred.Child.main(Child.java:249)

我测试了单输入路径,没问题。只有当我使用

MultipleInputs.addInputPath(job, TextInputpath, TextInputFormat.class,
            TextMapper.class);
    MultipleInputs.addInputPath(job, ImageInputpath,
            WholeFileInputFormat.class, ImageMapper.class); 

我用谷歌搜索并找到了这个链接https://issues.apache.org/jira/browse/MAPREDUCE-1178,它说 0.21 有这个错误。但我使用的是 1.0.3,这个 bug 又回来了。任何人都有同样的问题,或者任何人都可以告诉我如何解决它?谢谢

这是image mapper的设置代码,第4行是出错的地方:

protected void setup(Context context) throws IOException,
            InterruptedException {
        InputSplit split = context.getInputSplit();
        Path path = ((FileSplit) split).getPath();
        try {
            pa = new Text(path.toString());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

【问题讨论】:

  • 您能否发布 ImageMapper 类的代码 - 看起来您正试图在设置方法中将输入拆分转换为 FileInputSplit。
  • 我也有类似的问题.. 有解决办法吗?

标签: hadoop classcastexception


【解决方案1】:

我遇到了同样的问题,但实际问题是我在设置 MultipleInputs 后仍在设置 InputFormat:

job.setInputFormatClass(SequenceFileInputFormat.class);

删除这条线后一切正常。

【讨论】:

    【解决方案2】:

    根据我的评论,TaggedInputSplit 的 Javadocs 确认您可能错误地将输入拆分转换为 FileSplit:

    /**
     * An {@link InputSplit} that tags another InputSplit with extra data for use
     * by {@link DelegatingInputFormat}s and {@link DelegatingMapper}s.
     */
    

    我的猜测是你的设置方法看起来像这样:

    @Override
    protected void setup(Context context) throws IOException,
            InterruptedException {
        FileSplit split = (FileSplit) context.getInputSplit();
    }
    

    不幸的是,TaggedInputSplit 不公开可见,因此您不能轻松地进行instanceof 样式检查,然后进行强制转换,然后调用TaggedInputSplit.getInputSplit() 以获取实际的底层 FileSplit。因此,要么您需要自己更新源代码并重新编译和部署,发布 JIRA 票证以要求在未来版本中修复此问题(如果尚未在 2+ 中执行此操作)或执行一些讨厌的 nasty 反射黑客来获取底层 InputSplit

    这是完全未经测试的:

    @Override
    protected void setup(Context context) throws IOException,
            InterruptedException {
        InputSplit split = context.getInputSplit();
        Class<? extends InputSplit> splitClass = split.getClass();
    
        FileSplit fileSplit = null;
        if (splitClass.equals(FileSplit.class)) {
            fileSplit = (FileSplit) split;
        } else if (splitClass.getName().equals(
                "org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit")) {
            // begin reflection hackery...
    
            try {
                Method getInputSplitMethod = splitClass
                        .getDeclaredMethod("getInputSplit");
                getInputSplitMethod.setAccessible(true);
                fileSplit = (FileSplit) getInputSplitMethod.invoke(split);
            } catch (Exception e) {
                // wrap and re-throw error
                throw new IOException(e);
            }
    
            // end reflection hackery
        }
    }
    

    反射黑客解释:

    由于 TaggedInputSplit 被声明为受保护范围,它对 org.apache.hadoop.mapreduce.lib.input 包之外的类不可见,因此您不能在 setup 方法中引用该类。为了解决这个问题,我们执行了一些基于反射的操作:

    1. 检查类名,我们可以使用它的完全限定名来测试类型 TaggedInputSplit

      splitClass.getName().equals("org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit")

    2. 我们知道我们要调用TaggedInputSplit.getInputSplit() 方法来恢复包装的输入拆分,所以我们利用Class.getMethod(..) 反射方法来获取对该方法的引用:

      Method getInputSplitMethod = splitClass.getDeclaredMethod("getInputSplit");

    3. 该类仍然不公开可见,因此我们使用 setAccessible(..) 方法覆盖它,阻止安全管理器抛出异常

      getInputSplitMethod.setAccessible(true);

    4. 最后,我们在对输入拆分的引用上调用该方法并将结果转换为 FileSplit(乐观地希望它是这种类型的实例!):

      fileSplit = (FileSplit) getInputSplitMethod.invoke(split);

    【讨论】:

    • 和你猜的一模一样。我试过你的代码,它现在可以工作了。你太专业了!
    • 我再次阅读了代码,但我仍然不确定它是如何工作的。你能简单解释一下反射黑客吗?谢谢。
    • 见最后添加的部分
    • 这个解决方法很棒,但是有没有人知道用 MultipleInputs 将信息从 Job 传递到 Mapper 的一种不那么笨拙的方法? FWIW,有issues.apache.org/jira/browse/MAPREDUCE-2226
    • 您希望传递什么样的信息 - 通过配置传递有什么问题?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-31
    • 2013-03-09
    • 2018-02-24
    • 1970-01-01
    相关资源
    最近更新 更多