hadoop生态系统之编写自己的mapreduce

mapreduce的过程介绍

注意:下面的内容中RM=ResourceManager ,NM=NodeManager

step 1: client -> RM

这是提交job的流程,client端先向RM申请一个ApplicationId,RM进行内部处理包括资源分配,优先级设定之类的准备工作.
等到ApplicationId后,client端提交程序到RM执行。 这个提交过程会指明localfile,jars ,输入,输出,环境变量等参数,
实际上跟命令行bin/hadoop jar执行的东西一样.RM接收到提交后,根据资源(CPU,内存,硬盘,网络 ) 来进行调度.

RM的调度流程是:RM不断接收NM的资源使用报告,使用调度算法,来分配container给应用程序使用。

step 2:RM -> ApplicationMaster

RM会启动一个container来作为Applicationmaster。这个Applicationmaster包含了client提交过来的所有参数。client可以通过RM来获取application的状态信息.ApplicationMaster虽然叫master但是它不在RM端,跟其它container一样分布在各个slave,与之相对的是mapreduce1中的JobTracker,JobTracker单点部署,是所有Map-reduce业务的集中处理点,瓶颈很明显。
ApplicationMaster 的职责有:向RM请求的资源容器,运行任务,跟踪应用程序的状态和监控它们的进程,处理任务的失败原因。

step 3:ApplicationMaster -> NM

ApplicationMaster会传递必要的上下文参数到NM来为任务启动一个container,Container执行具体的mapreduce业务,向ApplicationMaster报告执行情况。
NM负责维护该Container状态,并向RM提交健康报告,资源使用情况

整个过程围绕ContainerManager类来进行。

 

step 4:任务结束

成功或者失败,ApplicationMaster通知RM任务结束,进行资源回收等工作。

编写自己的mapreduce

mapreduce计算模型介绍

MapReduce首先对输入的文件按行处理,此时的处理函数称为map,参数的格式为(null,value1)。
Map函数输出格式为list(key2,value2)的结果作为Reduce函数的输入,并且相同 key2 的记录汇聚到同一 reduce,最终产生list(value3) 的输出结果。

简单表示如下:
Map: (null,value1) ->list(key2,value2)
Reduce:(key2,list(value2)) ->list(value3)
如果有combiner的话,每一个Map函数输出到reduce之前,还会被combiner一次,相同 key2 的记录会被combine起来再输出到reduce步骤, combiner步骤能够减少数据传输量。

每个reduce产生一个输出文件,这是因为hdfs不支持并发写。

选一个框架编写自己的mapreduce

这里我选择的是运行比较慢的python语言的mrjob框架。虽然用原生的java是效率最高的,
但是python在科学计算,人工智能,生物工程方面有着大量的现成模块。在大数据处理领域,潜力无限。
同时mrjob对AWS支持得非常好,又有Yelp的强力支持目前也是发展的最快的python mr框架.
mrjob的详细文档网址: https://github.com/Yelp/mrjob/blob/master/docs/

1, 安装mrjob

最简单的方法:
pip install mrjob
mrjob依赖的模块是:pyyaml,boto-develop
使用前配置环境变量 HADOOP_HOME=你的hadoop位置
一个map-reduce类继承自MRJob类,通过重写mapper,combiner,reducer迭代函数和OUTPUT_PROTOCOL属性等来实现自己的业务。
假设hdfs上有输入文件/1内容如下:
sdafsfd 1984 4
sdafsfd 1984 4
sdafsfd 1987 4
sdafsfd 1984 4
sdafsfd 1985 4
mrjob有2种模式的job,先看第一种:One-step jobs

创建文件mrsample_onestep.py 内容如下:

#! /usr/bin/env python
import os
import re
from mrjob.job import MRJob
from mrjob.protocol import RawProtocol, ReprProtocol
class MRSample(MRJob):

    # 设置序列化的方式,mrjob的运行时间很大部分花在序列化/反序列化上,这是很重要的优化方式
    OUTPUT_PROTOCOL = RawProtocol #text

    # mapper函数之前的初始化操作,可以为mapper函数提供一些辅助变量
    def mapper_init(self):
        pass

    # key 在这里是none line就是输入文件的一行数据
    def mapper(self, key, line):
        data = line.split('\t')

        # 分割后数据量小于3个 则不处理直接return
        if len(data) < 3:
            return

        # 根据数据结构提取数据
        info = data[0]
        year = data[1]
        count = int(data[2])

     
        k = [info,year]

        #传输的时候都会序列化,所以k可以是任意可序列化的对象
        yield (k, count)

    #
    def combiner(self, key, counts):
        # the combiner must be separate from the reducer because the input
        # and output must both be JSON
        yield (key, sum(counts))

    def reducer(self, key, counts):
        # 迭代输出结果
        yield "%s\t%s" % str(key), str(sum(counts))

if __name__ == '__main__':
    # 命令行的下调用类方法run()来运行
    MRSample.run()

 

mrjob提供了几种代码运行的方式,
1)在emr上运行 -r emr
2)在本地模拟hadoop的运行 -r local
3)在hadoop集群上运行 -r hadoop

运行
./mrsample_onestep.py -r hadoop /1 /result
将会把结果输出到/result

mrjob还提供了定义multi-step jobs的功能,覆盖steps()函数即可完成,steps()函数返回一个数组的长度
表示了整个mr过程将会有几个步骤。
下面代码展示这一过程:

class MRSteps(MRJob):
    """额外的step检查结果是奇数还是偶数"""

    def get_words(self, _, line):
        for word in line.split():
            yield word.lower(), 1

    def sum_words(self, word, counts):
        yield word, sum(counts)

    def mod_counts(self, word, counts):
        yield word, counts%2

    def steps(self):
        return [self.mr(mapper=self.get_words,
                        combiner=self.sum_words,
                        reducer=self.sum_words),
                self.mr(mapper=self.mod_counts)]
if __name__ == '__main__':
    # 命令行的下调用类方法run()来运行
    MRSteps.run()

 

posted @ 2013-09-29 15:02  nosqlcn  阅读(399)  评论(0编辑  收藏  举报