Hadoop平台技术介绍潘司群北京邮电大学计算机学院网络工程专业soulpsq@gmail.com摘要:本文主要介绍了hadoop平台的两个关键技术,分布式文件系统(HDFS)和分布式计算系统(map/reduce)。通过介绍其技术演进、核心算法、主要应用场景、所能解决问题以及发展状况等来大致展开文章内容,为今后实践学习相关知识积累了一定的理论基础。我参考了很多文章,但在本文中并没有任何原文抄袭,所有语言都均由笔者自己组织,有部分数据(两到三处)为引用而来也注明了出处。与此同时,一定也会有很多解释欠妥甚至错误的地方,并且有很多原理理解得还很肤浅,无法做出更加深入的解释,这些都希望学长能够给出一些建议,我也希望在今后的实践中能加深对所学理论知识的理解。引言:随着google在文件系统(GFS)、数据库(BigTable)以及并行计算(MapReduce)领域的三大论文的问世,云计算也终于从一个模糊的概念走向了技术的实体化。不过google并没有再接再厉公布其技术细节与核心,于是就催生了hadoop这样一个开源平台的出现。说起hadoop,不得不提一下它的创始人DougCutting,他是著名的全文检索引擎lucene(国内的很多项目都有该技术的影子)和搜索引擎nutch的发起者,当然它们也都是开源的。正是由于DougCutting等人的之前的工作,才使得我们能够有机会接触并学习到搜索引擎等曾经神秘莫测的技术。总的来说,Hadoop其实就是google技术的开源平台,hadoop的分布式文件系统HDFS、数据库系统Hbase以及分布式计算系统Map/Reduce(这个名都没变···)就是对google的GFS,BigTalbe以及MapReduce的开源实现,在本文中,我主要介绍的是组成Hadoop的最为重要的HDFS和Map/Reduce两项关键技术。一.分布式计算框架Map/ReduceMap/Reduce是一个分布式的编程模型,它源自于函数式的编程思想,函数式编程并不是一种编程语言,也没有固定不变的模式,它很多的时候指的是一种编程的理念,在严格的函数式编程中,函数几乎被用于一切,甚至是最简单的变量计算,但是在实际中,函数式风格的代码往往是作为一部分而为整体程序来服务,有关于函数式编程更详细的内容,推荐阅读参考文章[4]。让我们回到Map/Reduce,顾名思义,MapReduce由两个过程组成,即map(映射)和reduce(规约),并且map在前,reduce在后。让我们先举个简单的例子来解释这两个过程:一维数组M=[1,2,3,4,5]在乘2的条件下映射为[2,4,6,8,10],当继续进行数组元素求和时,则规约为30,整个过程其实就是将∑m[i]*2这样一个运算拆分成了两步来执行。从上面的例子我们了解到,MapReduce是一种先分后合的编程模式,即先将任务拆分成若干子任务,然后子任务分别执行,最后将所有子任务的执行结果进行合并规约得出最终的输出结果(0个或1个最终输出结果),这就是MapReduce编程模式的总体思路,实际运用中将会复杂得多。在讲述具体的实现过程之前,我认为有必要对出现的一些相关术语做一些简单的说明,这样会方便大家顺利地理解后面的内容。Hadoop的MapReduce和Google的MapReduce就如其名基本不变一样(就是前缀公司不同···),框架结构基也本都是一样的,但本着开源就要开得不一般,就要不走寻常路的的出发点,hadoop还是将其中明明一样的东西硬给安了个不一样的名字,可能是因为我最开始读的是Google的文章而先入为主了,导致再去读hadoop的相关文章时,总觉得hadoop的术语命名又冗长又不形象,所以在这里就先把术语统一下,以防以后阅读其他文章时弄乱,我在网上找到了一个术语对照表,觉得还不错,所以就直接拿来主义了哈。中文翻译Google术语Hadoop术语相关解释作业JobJob用户的每一个计算请求,就称为一个作业。作业服务器MasterJobTracker用户提交作业的服务器,同时,它还负责各个作业任务的分配,管理所有的任务服务器任务服务器WorkerTasktrackerGoogle的命名很贴切,就是任劳任怨的工人,负责执行具体的任务任务TaskTask每一个作业都需要拆分开,交由多个服务器来完成,拆分出来的执行单位,就称为任务总算进入了正题,下面就没有什么能妨碍我们去探索MapReducede的秘密了!MapReduce编程模型中的函数接口是无数key/valuepair(键值对),其实现是通过输入的无数key/valuepair的集合输入来产生一个输出key/valuepair的集合作为最终输出结果。MapReduce函数的整个实现流程是:客户端(用户)向系统提交作业,作业被拆分成多个任务交由分配给map函数,map执行中间结果作为reduce的输入分配给reduce函数进行规约等处理,然后reduce函数输出最终结果。当用户提交作业给系统时,系统调用MapReduce程序进行处理,处理过程如下首先,MapReduce库函数将用户的输入文件分成M个数据片段,然后在其服务器机群中创建无数个程序副本来执行之后分配的任务,其中一个服务器将被作为JobTracker服务器来为空闲的TaskTracker服务器分配一个map或reduce任务并且负责管理TaskTracker服务器的工作状态,其他被分配程序副本的服务器将被作为TaskTracker服务器负责执行JobTracker分配的具体的工作(可能在选择哪个服务器充当JobTracker有某些策略,不过我没有查到)。现在假设有M个map任务和R个reduce任务将被分配(MR)然后,被分配了map任务的TaskTracker服务器将从用户输入文件中读取出所有的数据片段,并从中解析出key/valuepair(键值对)。然后将这些key/valuepair传递给map函数,通过调用map函数输出中间过程的key/valuepair集合,并写入内存的缓冲区。之后缓冲区的数据将被partition(分区)函数分成R个区域,然后被周期性地写入该TaskTracker服务器的本地磁盘上,并将其存储位置通知JobTracker服务器。之后,JobTracker服务器将中间输出数据的存放地址告知被分配了reduce任务的TaskTracker服务器,TaskTracker服务器通过RPC协议从该地址指向的存储中间输出的磁盘上读取数据。读取完数据之后,reduceTaskTracker服务器对输入数据进行归并排序处理,即将大量具有相同key值的key/valuepair进行合并(就是合并value),如果输入数据过多导致在内存中无法全部排序,也可在磁盘中进行。之后将排序后的数据传递给reduce函数,reduce函数将输出最终结果并保存在响应的分区文件上(每个分区只会产生一个分区文件)。最后,所有输出的结果都将保存在R个分区的输出文件上,以供下次调用,并通知客户端MapReduce程序运行完毕。以上就是MapReduce程序对客户端输入数据的处理过程,为了加深一下印象,举一个在Google的那篇论文里提到的程序:计算一个大的文档集合中每个单词出现的次数,原文中的伪码如下:Map函数部分:map(Stringkey,Stringvalue)://key:documentname//value:documentcontentsforeachwordwinvalue:Emitlntermediate(w,“1”);注:输出中间结果的函数Reduce函数部分:reduce(Stringkey,Iteratorvalues)://key:aword//values:alistofcountsIntresult=0;foreachvinvalues:result+=Parselnt(v);注:java函数,应该是计算每个单词的频率Emit(AsString(result))注:输出最终结果比如说我们有两篇文章M和N,内容分别是:M:“LiLeiisabeginnerinthefield,soisHanMeimei.”N:“Whatisthecloudcomputingatall?”文章M通过调用map函数的中间输出文件为:Li,1Lei,1Is,1a,1beginner,1in,1this,1field,1so,1is,1Han,1Meimei,1文章N的中间输出文件同理。而reduce函数输出的是每个单词以及出现序列,比如上面得M,N文件通过调用reduce函数的输出就是:(“Li”,[1]),(“Lei”,[1]),(“is”,[1,1,1]),(“a”,[1])等等然后计算每个单词出现的总次数,最终的输出结果就是:(“is”,3),(“the”,2),(“Li”,1),(“Lei”,1)等等。通过以上介绍,我们大致了解了MapReduce程序中map函数和reduce函数的运行过程,在实际的应用中,其实还有一些细节部分的改进使MapReduce框架机制能更好的适应程序的需要并拥有了更强大的性能,下面我们就介绍其中的一些帮助提高map函数和reduce函数性能的技术(由于理解有限,我就介绍一些自认为理解一些的方面)。1.为了减少网络IO的负载,JobTracker服务器在分配map任务时一般都会将map任务分配给那些在本地磁盘存放用户输入文件的TaskTracker服务器,这样会很大程度的减少因为传输文件所造成的网络IO负载,即使这样的TaskTracker服务器正处于工作状态无法接受新的任务,JobTracker也会将map任务分配给位置较近的JobTracker服务器机群(节点位置可以通过IP得知)。因为相比于传递数据,开发者认为传递计算会更有效率。2.在分配任务时,JobTracker服务器并不会将map任务或者reduce任务仅仅分配给一个TaskTracker服务器,而是会将一个map或reduce任务分给多个TaskTracker服务器去执行,这样的好处就是当一个TaskTracker服务器崩溃了,或者是数据传输出现错误时不会就直接导致任务失败,而是会由其他的服务器去完成(我记得有一篇文章里提到了在传输的数据里用检错码和纠错码以防止数据传输错误的情况,具体的实现细节没有记清)。当一个map或reduce任务被第一次提交完成的时候,其他正在执行相同任务的TaskTracker服务器就会停止执行而转入空闲状态。因为JobTracker服务器是一个存储所有TaskTraker服务器状态,并为各个TaskTracker服务器传递信息,并负责所有任务的调度,它处着一个决策者得地位。因此,为了防止JobTracker服务器崩溃而导致整个数据处理失败,JobTracker服务器通常会定时将内部信息以日志的形式发给另外几台JobTracker服务器来作为服务器备份,并且也会在本机上定期将数据写入磁盘,并设置检查点。3.由MapReduce程序的原理我们知道,用户输入数据请求的完成效率遵循木桶原理,即取决于最后的map或reduce任务的完成时间。因此,为了防止某些TaskTracker服务器不给力,执行map或reduce任务速度特别慢,开发人员还设置了备用任务制度,就是当MapReduce的所有任务快要完成时,对于一些速度很慢还未执行完的任务,JobTracker服务器会分配一些备用的TaskTracker服务器进程来执行该任务,无论是备用服务器还是之前的服务器先完成了这个任务,该任务都将被标记成已完成,其他TaskTracker服务器会停止执行而进入空闲状态,我在一篇文章中也读到了另外一种机制,就是JobTracker没有预先设置备用TaskTracker服务器,而是由最先完成任务而转入空闲状态的TaskTracker服务器去帮助执行速度慢的任务,不过两者的目标是一样的。4.在上面所举的例子中,当文件内容十分巨大时,可能会出先成千上