我正在许多输入文件上运行hadoop作业。 但是如果其中一个文件被破坏,整个工作都会失败。
如何让作业忽略损坏的文件? 也许给我写一些计数器/错误日志,但不会失败整个工作
答案 0 :(得分:7)
这取决于你的工作失败的地方 - 如果一行损坏,并且你的map方法中某处抛出了异常,那么你应该能够用try / catch包装你的map方法的主体而只是记录错误:
protected void map(LongWritable key, Text value, Context context) {
try {
// parse value to a long
int val = Integer.parseInt(value.toString());
// do something with key and val..
} catch (NumberFormatException nfe) {
// log error and continue
}
}
但是如果您的InputFormat的RecordReader抛出错误,那么您需要修改映射器run(..)
方法 - 默认实现如下:
public void run(Context context) {
setup(context);
while (context.nextKeyValue()) {
map(context.getCurrentKey(), context.getCurrentValue(), context);
}
cleanup(context);
}
所以你可以修改它来尝试在context.nextKeyValue()
调用上捕获异常,但你必须小心忽略读者抛出的任何错误 - 例如IOExeption可能不是'可跳过的'只是忽略错误。
如果您编写了自己的InputFormat / RecordReader,并且您有一个特定的异常表示记录失败但允许您跳过并继续解析,那么这样的事情可能会起作用:
public void run(Context context) {
setup(context);
while (true) {
try {
if (!context.nextKeyValue()) {
break;
} else {
map(context.getCurrentKey(), context.getCurrentValue(), context);
}
} catch (SkippableRecordException sre) {
// log error
}
}
cleanup(context);
}
但只是重新改写 - 你的RecordReader必须能够在出错时恢复,否则上面的代码可能会让你陷入无限循环。
针对您的具体情况 - 如果您只是想在第一次失败时忽略文件,那么您可以将run方法更新为更简单的方法:
public void run(Context context) {
setup(context);
try {
while (context.nextKeyValue()) {
map(context.getCurrentKey(), context.getCurrentValue(), context);
}
cleanup(context);
} catch (Exception e) {
// log error
}
}
警告的最后一句话:
答案 1 :(得分:2)
故障陷阱用于级联:
每当操作失败并抛出异常时,如果存在关联的陷阱,则会将违规的元组保存到陷阱Tap指定的资源中。这允许作业继续处理而不会丢失任何数据。
这实际上会让您的工作继续下去,让您稍后检查损坏的文件
如果您对流程定义语句中的级联有些熟悉:
new FlowDef().addTrap( String branchName, Tap trap );
答案 2 :(得分:0)
还有另一种可能的方式。您可以使用mapred.max.map.failures.percent
配置选项。当然,解决这个问题的方法也可以隐藏在映射阶段发生的其他一些问题。