- 我想将来自同一个reducer的两种不同类型的输出写入两个不同的目录
我可以使用hadoop中的多输出特性来写入不同的文件,但它们都属于同一个输出文件夹。
我想把同一个reduce中的每个文件写到不同的文件夹中。
有办法做到这一点吗?
如果我尝试将例如"hello/testfile"作为第二个参数,它将显示invaid参数。所以我无法写入不同的文件夹。
- 如果以上情况不可能,映射程序是否可以从输入文件夹中只读取特定文件
请帮帮我。
提前感谢!
谢谢你的回复。我能够使用上述方法成功地读取文件。但在分布式模式下,我无法做到这一点设置:
mos.getCollector("data", reporter).collect(new Text(str_key), new Text(str_val));
(使用多个输出,在Job Conf中:我试过使用
FileInputFormat.setInputPaths(conf2, "/home/users/mlakshm/opchk285/data-r-00000*");
以及
FileInputFormat.setInputPaths(conf2, "/home/users/mlakshm/opchk285/data*");
但是,它给出了以下错误:
cause:org.apache.hadoop.mapred.InvalidInputException: Input Pattern hdfs://mentat.cluster:54310/home/users/mlakshm/opchk295/data-r-00000* matches 0 files
问题1:将输出文件写入不同的目录-可以使用以下方法:
1.使用MultipleOutputs类:
使用MultipleOutputs可以创建多个命名输出文件,这非常棒。正如您所知,我们需要将其添加到您的驱动程序代码中。
MultipleOutputs.addNamedOutput(job, "OutputFileName", OutputFormatClass, keyClass, valueClass);
API提供了两个重载的写方法来实现这一点。
multipleOutputs.write("OutputFileName", new Text(Key), new Text(Value));
现在,要将输出文件写入单独的输出目录,需要使用一个重载的写方法,并为基本输出路径添加一个额外的参数。
multipleOutputs.write("OutputFileName", new Text(key), new Text(value), baseOutputPath);
请记住在每个实现中更改baseOutputPath。
2.重命名/移动驱动程序类中的文件:
这可能是将输出写入多个目录的最简单的方法。使用multipleOutput并将所有输出文件写入单个输出目录。但每个类别的文件名都需要不同。
假设您想要创建3组不同的输出文件,第一步是在驱动程序中注册命名的输出文件:
MultipleOutputs.addNamedOutput(job, "set1", OutputFormatClass, keyClass, valueClass);
MultipleOutputs.addNamedOutput(job, "set2", OutputFormatClass, keyClass, valueClass);
MultipleOutputs.addNamedOutput(job, "set3", OutputFormatClass, keyClass, valueClass);
此外,在驱动程序代码中创建不同的输出目录或所需的目录结构,以及实际的输出目录:
Path set1Path = new Path("/hdfsRoot/outputs/set1");
Path set2Path = new Path("/hdfsRoot/outputs/set2");
Path set3Path = new Path("/hdfsRoot/outputs/set3");
最后一个重要步骤是根据输出文件的名称重命名输出文件。如果工作成功;
FileSystem fileSystem = FileSystem.get(new Configuration);
if (jobStatus == 0) {
// Get the output files from the actual output path
FileStatus outputfs[] = fileSystem.listStatus(outputPath);
// Iterate over all the files in the output path
for (int fileCounter = 0; fileCounter < outputfs.length; fileCounter++) {
// Based on each fileName rename the path.
if (outputfs[fileCounter].getPath().getName().contains("set1")) {
fileSystem.rename(outputfs[fileCounter].getPath(), new Path(set1Path+"/"+anyNewFileName));
} else if (outputfs[fileCounter].getPath().getName().contains("set2")) {
fileSystem.rename(outputfs[fileCounter].getPath(), new Path(set2Path+"/"+anyNewFileName));
} else if (outputfs[fileCounter].getPath().getName().contains("set3")) {
fileSystem.rename(outputfs[fileCounter].getPath(), new Path(set3Path+"/"+anyNewFileName));
}
}
}
注意:这不会给作业增加任何显著的开销,因为我们只是将文件从一个目录移动到另一个目录。选择任何特定的方法都取决于实现的性质。
总之,这种方法基本上将使用不同名称的所有输出文件写入同一输出目录,当作业成功完成时,我们重命名基本输出路径,并将文件移动到不同的输出目录。
问题2:从输入文件夹读取特定文件:
您可以使用MultipleInputs类从目录中读取特定的输入文件。
根据您的输入路径/文件名,您可以将输入文件传递给相应的Mapper实现。
情况1:如果所有输入文件都在一个目录中:
FileStatus inputfs[] = fileSystem.listStatus(inputPath);
for (int fileCounter = 0; fileCounter < inputfs.length; fileCounter++) {
if (inputfs[fileCounter].getPath().getName().contains("set1")) {
MultipleInputs.addInputPath(job, inputfs[fileCounter].getPath(), TextInputFormat.class, Set1Mapper.class);
} else if (inputfs[fileCounter].getPath().getName().contains("set2")) {
MultipleInputs.addInputPath(job, inputfs[fileCounter].getPath(), TextInputFormat.class, Set2Mapper.class);
} else if (inputfs[fileCounter].getPath().getName().contains("set3")) {
MultipleInputs.addInputPath(job, inputfs[fileCounter].getPath(), TextInputFormat.class, Set3Mapper.class);
}
}
情况2:如果所有输入文件都不在一个目录中:
即使输入文件在不同的目录中,我们基本上也可以使用上面相同的方法。在基本输入路径上迭代,并检查文件路径名是否符合匹配条件。
或者,如果文件位于完全不同的位置,最简单的方法是单独添加到多个输入。
MultipleInputs.addInputPath(job, Set1_Path, TextInputFormat.class, Set1Mapper.class);
MultipleInputs.addInputPath(job, Set2_Path, TextInputFormat.class, Set2Mapper.class);
MultipleInputs.addInputPath(job, Set3_Path, TextInputFormat.class, Set3Mapper.class);
希望这能有所帮助!非常感谢。
将MultipleOutputs代码复制到您的代码库中,并放宽对允许字符的限制。无论如何,我看不出有任何合理的理由限制。
是的,您可以指定输入格式只处理某些文件:
FileInputFormat.setInputPaths(job, "/path/to/folder/testfile*");
如果你确实修改了代码,请记住,_SUCCESS文件应该在成功完成作业后写入两个文件夹-虽然这不是一个要求,但这是一种机制,有人可以通过它来确定该文件夹中的输出是否完整,而不是因为错误而被"截断"。
是的,你可以这样做。您所需要做的就是为来自reducer的特定键/值对生成文件名。
如果重写一个方法,则可以根据获得的键/值对返回文件名,依此类推。
https://www.google.co.in/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CFMQFjAA&url=https%3A%2F%2Sites.google.com%2Site%2Fhadoopandhive%2Fhome%2Fhow to write output to multipletextoutputformat&ei=y7YBULarN8iIrAf4iPSOBg&usg=AFQjCNHbd8sRwlY1-My2gNYI0yqw4254YQ