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()