我在同一个文件夹中有数百到数千个输入文件(.csv)和元数据文件(.json)$HDFS_ROOT/inputFolder
//输入数据.csv文件
input_1.csv,input_2.csv..input_N.csv
//输入元数据.json文件
input_1.json,input_2.json..input_N.json
有人能告诉我如何让每个映射器获得一个文件对,即一个完整的输入文件(.csv)和它的元数据文件(.json)吗?
注意:input_i.csv和input_i.json应该转到同一个映射器,这样输入及其元数据对验证都有意义。
我尝试过的:我尝试使用WholeFileInputFormat和WholeFileRecordReader,它们分别是从FileInput Format和RecordReader扩展而来的。这只适用于.csv文件。此外,我将.json文件放在分布式缓存中,以便映射器可以访问。这不是一个好的解决方案。
在不使用昂贵的Reducer的情况下解决此问题的关键是InputSplits。每个InputFormat都有方法getSplits,单个分割是单个映射器的输入,映射器的数量与InputSplits的数量一样多。在映射程序中,可以访问InputSplit:的实例
@Override
protected void setup(Context context) throws IOException, InterruptedException {
System.out.println("TRACE 1 " + context.getConfiguration().getClass().getName());
System.out.println("TRACE 2 " + context.getTaskAttemptID().toString());
System.out.println("TRACE 3 " + context.getInputSplit().toString());
}
基于此,我过去使用过三种方法:
1) context.getInputSplit()返回FileSplit的一个实例,该实例具有Path getPath()方法。但您必须注意CombineFileSplit和TaggedInputSplit它们可能会围绕FileSplit。使用CombineFileSplit,如果您不覆盖comineFileInputFormat.pools周围的默认行为,那么您就有可能在同一映射器中混合具有不同结构的记录而无法区分它们;
2) 一种更简单的方法是使用context.getInputSplit().toString(),返回的字符串将包含InputSplit所附加的路径,与MultipleInputs配合使用效果良好,但与CombineFileInputFormat不配合。它有点脏,因为您受toString()方法的支配,不建议将其用于生产系统,但对于快速原型来说已经足够好了;
3) 要定义您自己的代理InputFormat和InputSplit的实现,类似于MultipleInputs方法使用的方法,它依赖于DelegatingInputFormat该方法封装了可以读取数据的InputFormat的输入拆分,但将它们放在TaggedInputSplit中,请参阅源代码。在您的情况下,您可以在自己的InputFormat和InputSplits中隐藏元数据逻辑,并使映射器不知道如何将文件与元数据匹配。此外,您还可以直接将输入路径与元数据相关联,而不依赖于命名约定。这种方法非常适合生产系统。