Hadoop Streaming作业中的并行映射器任务数

时间:2015-06-18 09:04:58

标签: hadoop hadoop-streaming

我刚刚开始了解Hadoop。我正在尝试将流接口与处理文件的Python脚本结合使用:对于每个输入文件,我创建一个包含一些信息的输出文件,因此这是一个没有reducer的map作业。我发现文件正在逐个处理,这不是我想要的。

我会解释我所做的事情,但我会在事后发布一些代码,以防我在那里遗漏一些东西。

我有一个输入格式和记录阅读器,可以读取整个文件并将其内容用作值和文件名作为键。 (文件并不大。)另一方面,我有一个输出格式和记录编写器,它将值写入名称基于键的文件。我正在使用-io rawbytes,我的Python脚本知道如何读取和写入键/值对。

就产生我期望的输出而言,一切正常。如果我使用例如10个输入文件,我会得到10个分割。这意味着每次我的脚本运行时它只获得一个键/值对 - 这不是理想的,但这不是什么大问题,我可以看出这可能是不可避免的。不太好的是,任何时候只有一个脚本运行实例。设置mapreduce.job.maps没有任何区别(虽然我依稀记得看到有关此值的内容只是一个建议,所以也许Hadoop做出了不同的决定)。我错过了什么?

这是我的代码: -

#!/bin/bash

hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming.jar \
    -libjars mimi.jar \
    -D mapreduce.job.reduces=0 \
    -files rawbytes_mapper.py,irrelevant.py \
    -inputformat "mimi.WholeFileInputFormat" \
    -outputformat "mimi.NamedFileOutputFormat" \
    -io rawbytes \
    -mapper "rawbytes_mapper.py irrelevant blah blah blah" \
    -input "input/*.xml" \
    -output output
#!/usr/bin/python

def read_raw_bytes(input):
    length_bytes = input.read(4)
    if len(length_bytes) < 4:
        return None
    length = 0
    for b in length_bytes:
        length = (length << 8) + ord(b)
    return input.read(length)

def write_raw_bytes(output, s):
    length = len(s)
    length_bytes = []
    for _ in range(4):
        length_bytes.append(chr(length & 0xff))
        length = length >> 8
    length_bytes.reverse()
    for b in length_bytes:
        output.write(b)
    output.write(s)

def read_keys_and_values(input):
    d = {}
    while True:
        key = read_raw_bytes(input)
        if key is None: break
        value = read_raw_bytes(input)
        d[key] = value
    return d

def write_keys_and_values(output, d):
    for key in d:
        write_raw_bytes(output, key)
        write_raw_bytes(output, d[key])

if __name__ == "__main__":
    import sys
    module = __import__(sys.argv[1])
    before = read_keys_and_values(sys.stdin)
    module.init(sys.argv[2:])
    after = module.process(before)
    write_keys_and_values(sys.stdout, after)
package mimi;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileSplit;
import org.apache.hadoop.mapred.InputSplit;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.RecordReader;
import org.apache.hadoop.mapred.Reporter;

public class WholeFileInputFormat extends FileInputFormat<BytesWritable, BytesWritable>
{
    private static class WholeFileRecordReader implements RecordReader<BytesWritable, BytesWritable>
    {
        private FileSplit split;
        private JobConf conf;
        private boolean processed = false;

        public WholeFileRecordReader(FileSplit split, JobConf conf)
        {
            this.split = split;
            this.conf = conf;
        }

        @Override
        public BytesWritable createKey()
        {
            return new BytesWritable();
        }

        @Override
        public BytesWritable createValue()
        {
            return new BytesWritable();
        }

        @Override
        public boolean next(BytesWritable key, BytesWritable value) throws IOException
        {
            if (processed)
            {
                return false;
            }

            byte[] contents = new byte[(int) split.getLength()];
            Path file = split.getPath();
            String name = file.getName();
            byte[] bytes = name.getBytes(StandardCharsets.UTF_8);
            key.set(bytes, 0, bytes.length);
            FileSystem fs = file.getFileSystem(conf);
            FSDataInputStream in = null;
            try
            {
                in = fs.open(file);
                IOUtils.readFully(in, contents, 0, contents.length);
                value.set(contents, 0, contents.length);
            }
            finally
            {
                IOUtils.closeStream(in);
            }

            processed = true;
            return true;
        }

        @Override
        public float getProgress() throws IOException
        {
            return processed ? 1.0f : 0.0f;
        }

        @Override
        public long getPos() throws IOException
        {
            return processed ? 0l : split.getLength();
        }

        @Override
        public void close() throws IOException
        {
            // do nothing
        }
    }

    @Override
    protected boolean isSplitable(FileSystem fs, Path file)
    {
        return false;
    }

    @Override
    public RecordReader<BytesWritable, BytesWritable> getRecordReader(InputSplit split,
                                                                      JobConf conf,
                                                                      Reporter reporter)
    throws IOException
    {
        return new WholeFileRecordReader((FileSplit) split, conf);
    }
}
package mimi;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.mapred.lib.MultipleOutputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.RecordWriter;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.util.Progressable;

public class NamedFileOutputFormat extends MultipleOutputFormat<BytesWritable, BytesWritable>
{
    private static class BytesValueWriter implements RecordWriter<BytesWritable, BytesWritable>
    {
        FSDataOutputStream out;

        BytesValueWriter(FSDataOutputStream out)
        {
            this.out = out;
        }

        @Override
        public synchronized void write(BytesWritable key, BytesWritable value) throws IOException
        {
            out.write(value.getBytes(), 0, value.getLength());
        }

        @Override
        public void close(Reporter reporter) throws IOException
        {
            out.close();
        }
    }

    @Override
    protected String generateFileNameForKeyValue(BytesWritable key, BytesWritable value, String name)
    {
        return new String(key.getBytes(), 0, key.getLength(), StandardCharsets.UTF_8);
    }

    @Override
    public RecordWriter<BytesWritable, BytesWritable> getBaseRecordWriter(FileSystem ignored,
                                                                          JobConf conf,
                                                                          String name,
                                                                          Progressable progress)
    throws IOException
    {
        Path file = FileOutputFormat.getTaskOutputPath(conf, name);
        FileSystem fs = file.getFileSystem(conf);
        FSDataOutputStream out = fs.create(file, progress);
        return new BytesValueWriter(out);
    }
}

1 个答案:

答案 0 :(得分:0)

我想我可以帮助你完成这部分问题:

  

每次我的脚本运行时,它只获得一个键/值对 - 这不是理想的

如果 isSplitable 方法返回false,则每个映射器只处理一个文件。因此,如果您不会覆盖 isSplitable 方法并让它返回true ,您应该在一个映射器中拥有多个键/值对。在您的情况下,每个文件都是一个键/值对,因此即使 isSplitable 返回true,它们也无法拆分。

我无法弄清楚为什么一次只能启动一个映射器,但我还在考虑它:)