MapReduce踩坑笔记 - 讨厌的NullPointerException

最近在把同事的MR作业输出文件转成Parquet,因为占空间太大,结果踩了不少坑。。。各种 null,现在看到null我就生气,(╯^╰)

 

输出时使用的是

import org.apache.parquet.example.data.Group;

1.第一个null - Unable to initialize any output collector

原因:mapper输出类型不可序列化

这个问题在网上查了半天,大家众说纷纭,有的说内存不够之类的,最后查了MR作业的日志(查日志非常关键),发现是MapTask中createSortingCollector方法的报错,该方法主要部分如下:

int remainingCollectors = collectorClasses.length;
    for (Class clazz : collectorClasses) {
      try {
        if (!MapOutputCollector.class.isAssignableFrom(clazz)) {
          throw new IOException("Invalid output collector class: " + clazz.getName() +
            " (does not implement MapOutputCollector)");
        }
        Class<? extends MapOutputCollector> subclazz =
          clazz.asSubclass(MapOutputCollector.class);
        LOG.debug("Trying map output collector class: " + subclazz.getName());
        MapOutputCollector<KEY, VALUE> collector =
          ReflectionUtils.newInstance(subclazz, job);
        collector.init(context);
        LOG.info("Map output collector class = " + collector.getClass().getName());
        return collector;
      } catch (Exception e) {
        String msg = "Unable to initialize MapOutputCollector " + clazz.getName();
        if (--remainingCollectors > 0) {
          msg += " (" + remainingCollectors + " more collector(s) to try)";
        }
        LOG.warn(msg, e);
      }
    }
    throw new IOException("Unable to initialize any output collector");

大致就是会根据Mapper输出的结构在SerializationFactory中寻找对应的keySerializer,我在idea中Download source后,发现是我将mapper的输出设置为了<Void,Group>,而Group没有对应的Serializer,将Group更换为Writable类型的数据后问题解决。(具体原因还是要看日志是哪一行报错。

2.第二个null - parquet.example.schema should not be null

原因:writeSupport setSchema的位置错误。

看到这个错误我是肥肠郁闷的,因为我的代码里明明白白的写着:GroupWriteSupport.setSchema(schema, conf); 我思来想去,又查看了一下源码,也没有发现这个到底有什么问题,倒是同事的一句话提醒了我,我set了,拿到是空的,说明get的位置比set靠前,将该部分内容尽可能提前,在获得conf之后就设置模式,问题解决。

3.第三个null - java.lang.NullPointerException

原因:万恶的Combiner

非常标准的空指针异常,错误栈如下:

at org.apache.hadoop.mapred.IFile$Writer.append(IFile.java:190)
	at org.apache.hadoop.mapred.Task$CombineOutputCollector.collect(Task.java:1313)
	at org.apache.hadoop.mapred.Task$NewCombinerRunner$OutputConverter.write(Task.java:1630)
	at org.apache.hadoop.mapreduce.task.TaskInputOutputContextImpl.write(TaskInputOutputContextImpl.java:89)
	at org.apache.hadoop.mapreduce.lib.reduce.WrappedReducer$Context.write(WrappedReducer.java:105)
	at com.xiaomi.vip.hadoop.app.data.statistics.AppDataStatistics$IntSumReducer.reduce(AppDataStatistics.java:408)
	at com.xiaomi.vip.hadoop.app.data.statistics.AppDataStatistics$IntSumReducer.reduce(AppDataStatistics.java:382)
	at org.apache.hadoop.mapreduce.Reducer.run(Reducer.java:192)
	at org.apache.hadoop.mapred.Task$NewCombinerRunner.combine(Task.java:1651)
	at org.apache.hadoop.mapred.MapTask$MapOutputBuffer.sortAndSpill(MapTask.java:1637)
	at org.apache.hadoop.mapred.MapTask$MapOutputBuffer.flush(MapTask.java:1489)
	at org.apache.hadoop.mapred.MapTask$NewOutputCollector.close(MapTask.java:727)

注意到错误栈中出现了一个Combine,于是回去翻代码,发现同事原本的程序中有这样一句:


		job.setCombinerClass(IntSumReducer.class);

这句是做什么的呢? 我们知道map reduce 是分布式的,这句就是在reduce之前,将单个节点map之后的数据先做一次节点内的reduce,以提高效率,问题就出在这里,Combine 与 reduce使用的是相同的class,但由于parquet文件的输出需要,reduce时输出的数据结构为<Void,Group>,而reduce接受的结构为<Text,LongWritable>,这就导致先做了Combine的数据无法正常进行Reduce,这里就需要重写Combiner,或者向我一样,直接把这句干掉(对效率要求不高的懒人。

hhhh,至此我的job终于能欢快的打印百分比了,但能不能正常跑完,我也不知道,哈哈哈哈哈哈,如果还有坑,后续接着补充。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值