今天在跑两个关系运算的代码,结果接连两次都出现了同样的一个错误:
Error: java.io.IOException: Unable to initialize any output collector at org.apache.hadoop.mapred.MapTask.createSortingCollector(MapTask.java:412) at org.apache.hadoop.mapred.MapTask.access$100(MapTask.java:81) at org.apache.hadoop.mapred.MapTask$NewOutputCollector.<init>(MapTask.java:695) at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:767) at org.apache.hadoop.mapred.MapTask.run(MapTask.java:341) at org.apache.hadoop.mapred.YarnChild$2.run(YarnChild.java:163) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:415) at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1628) at org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:158)
错误都是出现在Mapper的task里面。错误说明输出的类型错误。百度了一把,发现有人提出这个问题,但是没有回答。
第一次出现的时候,通过查Job的代码,发现在setMapOutputKeyFormat方法里面,设定的输出是Text类型。但是在import里面却不是import的org.apache.hadoop.io.Text类,而是另外一个不知所云的包下面的Text。在修改成hadoop的Text类后,问题解决。
第二次出现就很奇葩了。写了一个RelationAlgebra的MapReduce类。其中Mapper的输出是自己写的一个Key类,而不是hadoop.io里面的已有的类,比如Text,LongWritable等等。在运行的时候有出现了 Unable to initialize any output collector的IOException。仔细检查了自己写的Mapper,Reducer和主Job程序,死活找不到问题出现在哪里。
这个输出的Key类是实现了WritableCompareble接口,其中的readField和write方法都没问题,compareTo也反复检查,自己写的hashCode方法也调测多遍。但是问题依旧。
无奈网上搜各种跟自己写Key类相关的帖子。终于在一个介绍自定义Key类的帖子里面发现了线索。链接http://blog.csdn.net/lastsweetop/article/details/9360075
在这个帖子里面有一句话,
/**
* 必须有默认的构造器皿,这样Mapreduce方法才能创建对象,然后通过readFields方法从序列化的数据流中读出进行赋值
*/
public EmploeeWritable() {
set(new Text(),new Text());
}
回头检查自己写的Key类,发现并没有显式地定义这个构造函数。在添加了无参数的构造函数后,问题解决。
在Hadoop 2.6.0的API文档中,对于WritableCompareble的介绍并没有特别指出这个要求。不知道是因为这个要求在以前的版本里面已经众所周知了,还是API作者忽视了。对于开始学习MapReduce编程的人来说,这个还是挺困惑的,因为log给出的错误几乎没有参考价值。