Mapreduce - 顺序作业?

时间:2013-10-21 18:00:31

标签: java google-app-engine mapreduce

我正在使用MapReduce(只是map,真的)分四个阶段完成数据处理任务。每个阶段都是一个MapReduce作业。我需要它们按顺序运行,也就是说,在第1阶段完成之前不要启动阶段2,等等。有没有人有这样的经验可以共享?

理想情况下,我们会在一夜之间完成这个4个工作序列,所以要做到这一点 cron-able也是一件好事。

谢谢

3 个答案:

答案 0 :(得分:1)

正如Daniel所提到的,appengine-pipeline库旨在解决这个问题。我在“实现您自己的管道作业”部分下一起查看mapreduce作业in this blog post

为方便起见,我会在此处粘贴相关部分:

既然我们知道如何启动预定义的MapreducePipeline,那么让我们来看看如何实现和运行我们自己的自定义管道作业。管道库提供了一个低级库,用于在appengine中启动任意分布式计算作业,但是,目前,我们将专门讨论如何使用它来帮助我们将mapreduce作业链接在一起。让我们扩展前面的例子,也输出一个字符和ID的反向索引。

首先,我们定义父管道作业。

class ChainMapReducePipeline(mapreduce.base_handler.PipelineBase):
def run(self):
    deduped_blob_key = (
    yield mapreduce.mapreduce_pipeline.MapreducePipeline(
        "test_combiner",
        "main.map",
        "main.reduce",
        "mapreduce.input_readers.RandomStringInputReader",
        "mapreduce.output_writers.BlobstoreOutputWriter",
        combiner_spec="main.combine",
        mapper_params={
            "string_length": 1,
            "count": 500,
        },
        reducer_params={
            "mime_type": "text/plain",
        },
        shards=16))

    char_to_id_index_blob_key = (
    yield mapreduce.mapreduce_pipeline.MapreducePipeline(
        "test_chain",
        "main.map2",
        "main.reduce2",
        "mapreduce.input_readers.BlobstoreLineInputReader",
        "mapreduce.output_writers.BlobstoreOutputWriter",
        # Pass output from first job as input to second job
        mapper_params=(yield BlobKeys(deduped_blob_key)),
        reducer_params={
            "mime_type": "text/plain",
        },
        shards=4))

这将启动与第一个示例相同的作业,从该作业获取输出,并将其提供给第二个作业,从而反转每个条目。请注意,第一个管道产量的结果将传递给第二个作业的mapper_params。管道库使用magic来检测第二个管道是否依赖于第一个管道完成,并且在deduped_blob_key解析之前不会启动它。

接下来,我必须创建BlobKeys帮助程序类。起初,我认为这不是必要的,因为我可以做到:

mapper_params={"blob_keys": deduped_blob_key},

但是,由于两个原因,这不起作用。第一个是“发电机管道不能直接访问它产生的子管道的输出”。上面的代码将要求生成器管道使用第一个作业的输出创建临时dict对象,这是不允许的。第二个是BlobstoreOutputWriter返回的字符串格式为“/ blobstore /”,但BlobstoreLineInputReader只需要“”。为了解决这些问题,我做了一个小帮手BlobKeys类。您会发现自己为许多工作执行此操作,并且管道库甚至包含一组常见的包装器,但它们不能在MapreducePipeline框架内工作,我将在本节的底部讨论。

class BlobKeys(third_party.mapreduce.base_handler.PipelineBase):
  """Returns a dictionary with the supplied keyword arguments."""

  def run(self, keys):
    # Remove the key from a string in this format:
    # /blobstore/<key>
    return {
        "blob_keys": [k.split("/")[-1] for k in keys]
    }

以下是map2和reduce2函数的代码:

def map2(data):
    # BlobstoreLineInputReader.next() returns a tuple
    start_position, line = data
    # Split input based on previous reduce() output format
    elements = line.split(" - ")
    random_id = elements[0]
    char = elements[1]
    # Swap 'em
    yield (char, random_id)

def reduce2(key, values):
    # Create the reverse index entry
    yield "%s - %s\n" % (key, ",".join(values))

答案 1 :(得分:0)

我不熟悉google-app-engine,但是你不能把所有的工作配置放在一个主程序中,然后按顺序运行它们吗?像下面这样的东西?我认为这适用于普通的map-reduce程序,所以如果google-app-engine代码没有太大的不同,它应该可以正常工作。

Configuration conf1 = getConf();
Configuration conf2 = getConf();
Configuration conf3 = getConf();
Configuration conf4 = getConf();

//whatever configuration you do for the jobs

Job job1 = new Job(conf1,"name1");
Job job2 = new Job(conf2,"name2");
Job job3 = new Job(conf3,"name3");
Job job4 = new Job(conf4,"name4");

//setup for the jobs here

job1.waitForCompletion(true);
job2.waitForCompletion(true);
job3.waitForCompletion(true);
job4.waitForCompletion(true);

答案 2 :(得分:0)

你需要appengine-pipeline项目,这正是为了这个目的。