如何在mapper或reducer中运行外部程序,将HDFS文件作为输入并将输出文件存储在HDFS中?

时间:2013-05-02 18:42:48

标签: hadoop mapreduce

我有一个外部程序,它将文件作为输入并提供输出文件

     //for example 
     input file: IN_FILE
     output file: OUT_FILE

    //Run External program 
     ./vx < ${IN_FILE} > ${OUT_FILE}

我想要HDFS中的输入和输出文件

我有8个节点的集群。我有8个输入文件,每个有1行

    //1 input file :       1.txt 
           1:0,0,0
    //2 input file :       2.txt 
           2:0,0,128
    //3 input file :       3.txt 
           3:0,128,0
    //5 input file :       4.txt 
           4:0,128,128
    //5 input file :       5.txt 
           5:128,0,0
    //6 input file :       6.txt 
           6:128,0,128
    //7 input file :       7.txt 
           7:128,128,0
    //8 input file :       8.txt 
           8:128,128,128

我正在使用KeyValueTextInputFormat

               key :file name
               value: initial coordinates

例如第5个文件

              key :5
              value:128,0,0

每个地图任务根据其初始坐标生成大量数据。

现在我想在每个map任务中运行外部程序并生成输出文件。

但我对如何使用HDFS中的文件感到困惑。

         I can use zero reducer and create file in HDFS 

         Configuration conf = new Configuration();
         FileSystem fs = FileSystem.get(conf);
         Path outFile;
         outFile = new Path(INPUT_FILE_NAME);
         FSDataOutputStream out = fs.create(outFile);

         //generating data ........ and writing to HDFS 
          out.writeUTF(lon + ";" + lat + ";" + depth + ";");

我很困惑如何使用HDFS文件运行外部程序而不将文件存入本地目录。

  with  dfs -get 

不使用MR我将获得带有shell脚本的结果,如下所示

#!/bin/bash

if [ $# -lt 2 ]; then
    printf "Usage: %s: <infile> <outfile> \n" $(basename $0) >&2
          exit 1
fi

IN_FILE=/Users/x34/data/$1
OUT_FILE=/Users/x34/data/$2                     

cd "/Users/x34/Projects/externalprogram/model/"

./vx < ${IN_FILE} > ${OUT_FILE}

paste ${IN_FILE} ${OUT_FILE} | awk '{print $1,"\t",$2,"\t",$3,"\t",$4,"\t",$5,"\t",$22,"\t",$23,"\t",$24}' > /Users/x34/data/combined
if [ $? -ne 0 ]; then
    exit 1
fi                      

exit 0

然后我用

运行它
         ProcessBuilder pb = new ProcessBuilder("SHELL_SCRIPT","in", "out"); 
         Process p = pb.start();

我非常感谢任何想法如何使用hadoop流或任何其他方式来运行外部程序。我想要HDFS中的INPUT和OUTPUT文件进行进一步处理。

请帮忙

2 个答案:

答案 0 :(得分:0)

因此,假设您的外部程序不知道如何识别或读取hdfs,那么您要做的是从java加载文件并将其作为输入直接传递给程序

Path path = new Path("hdfs/path/to/input/file");
FileSystem fs = FileSystem.get(configuration);
FSDataInputStream fin = fs.open(path);
ProcessBuilder pb = new ProcessBuilder("SHELL_SCRIPT");
Process p = pb.start();
OutputStream os = p.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(fin));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));

String line = null;
while ((line = br.readLine())!=null){
    writer.write(line);
}

输出可以以相反的方式完成。从进程中获取InputStream,并使FSDataOutputStream写入hdfs。

基本上,这两个东西的程序成为一个适配器,可以将HDFS转换为输入并输出回HDFS。

答案 1 :(得分:0)

你可以为此实现Hadoop Streaming:

$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \
-input myInputDirs \
-output myOutputDir \
-mapper myPythonScript.py \
-reducer /bin/wc \
-file myPythonScript.py \
-file myDictionary.txt

有关示例,请参阅https://hadoop.apache.org/docs/r1.0.4/streaming.pdf

也是一篇不错的文章:http://princetonits.com/blog/technology/hadoop-mapreduce-streaming-using-bash-script/

Hadoop流媒体是Hadoop发行版附带的实用工具。该实用程序允许您使用任何可执行文件或脚本作为映射器和/或reducer创建和运行Map / Reduce作业。

另一个例子:

$HADOOP_HOME/bin/hadoop  jar $HADOOP_HOME/hadoop-streaming.jar \
    -input myInputDirs \
    -output myOutputDir \
    -mapper /bin/cat \
    -reducer /bin/wc

在上面的示例中,mapper和reducer都是可执行文件,它们从stdin读取输入(逐行)并将输出发送到stdout。该实用程序将创建Map / Reduce作业,将作业提交到适当的群集,并监视作业的进度,直到完成为止。

当为映射器指定可执行文件时,每个映射器任务将在初始化映射器时将可执行文件作为单独的进程启动。当映射器任务运行时,它将其输入转换为行并将行提供给进程的标准输入。同时,映射器从进程的stdout收集面向行的输出,并将每一行转换为键/值对,并将其作为映射器的输出进行收集。默认情况下,直到第一个制表符的行的前缀是键,行的其余部分(不包括制表符)将是值。如果行中没有制表符,则整行被视为键,值为空。但是,这可以定制,如后面所述。

为reducers指定可执行文件时,每个reducer任务都会将可执行文件作为单独的进程启动,然后初始化reducer。当reducer任务运行时,它将其输入键/值对转换为行,并将行提供给进程的stdin。同时,reducer从进程的stdout收集面向行的输出,将每一行转换为键/值对,并将其作为reducer的输出收集。默认情况下,直到第一个制表符的行的前缀是键,行的其余部分(不包括制表符)是值。但是,这可以定制。