Hadoop源代码分析(完整版).pdf

所需积分/C币:16 2014-11-07 00:38:26 6.31MB PDF
14
收藏 收藏
举报

Hadoop源代码分析(完整版
这里,我把( bjectWritable标为红色,是因为相对于其他对象,它有不同的地位。当我们讨论 Hadoop的RPC时,我们会提到 RPC上交换的信息,必须是Java的基本类型, String和 Writab1e接山的实现类,以及元素为以上类型的数组。 ObjectWritable 对象保存了一个可以在R℃上传输的对象和对象的类型信息、。这样,我们就有了一个万能的,可以用于客户端/服务器间传输的 Writable对象。例如,我们要把上面例∫屮的对象作为RPC请求,需要根据 MyWritablc创建个0 bjectWritable 0 ject Writable往流里会写如下信息 对象类名长度,村类名,亲自已的出行化哭 这样,到∫对端,( b jectWr' itable可以根据对象类名创建对应的对象,并解串行。应该注意到, ObjectWritable依赖于 Writablefactories,那存储了 Writable子类对应的工厂。我们需要把 My Writable的工厂,保存在 Writablefactories中(通 i WritableFactories. sotFactory) Hadoop源代码分析(五) 介绍完org. apache. hadoop.io以后,我们开始来分析org. apache. hadoop.rpc。RPC采用客户机/服务器模式。请求程序就是一 个客户机,而服务提供程序就是一个服务器。当我们讨论HDFS的,通信可能发生在: Client-Namenode之间,其中 NameNode是服务器 Client-Datanode之间,其中 DataNode是服务器 Datanode- NameNode之间,其中 NameNode是服务器 Datanode- Datenode之间,其中某一个 Datenode是服务器,另一个是客户端 如果我们考虑 Hadoop的Map/ Reduce以后,这些系统间的通信就更复杂」。为」解决这些客户机/服务器之间的通信, Hadoop 引入了个RPC框架。该RPC框架利用的Java的反射能力,避免∫某些RC解决方案中需要根据某种接凵语言(如 CORBA的 ⊥DL)生成仔根和框架的问题。但是,该RPC框架要求调用的参数和返回结果必须是Java的基本类型, String和 Writable接 口的实现类,以及元素为以上类型的数组。同吋,接口方法应该只抛出 IOException异常。(参考自 http://zhangyu8371.javaeye.com/blog/86306) 既然是RPC,当然就有客户端和服务器,当然,org. apache. hadoop.rpc也就有了类 Client和类 Server但是类 Server是 个抽象类,类RPC封装了 Server,利用反射,把某个对象的方法开放出来,变成RPC中的服务器。 下图是org. apache, hadoop.rpc的类图。 Writable Generic writable Floarwritable Lougwrita Nmwa吧k Writable Faerie Writable Factor Map File Iatalnpurstream Exception ayatem PrimaryTypes Array File SenIle atalnput Buff DataOutputBt MuiplelOExeeption versionMisnahexeepti declass. writable pNoDelay boolean sckeltactory SocketFactory +getRemotelpo): InetSocketAddress -handlers cleanupCoamectiorn() Zeo Refernce +cal(in paran: Writable. i reveveTim: long): Writable in addr: Wrtab'e mmerticenlist tallin param: writable. in aulkheses: net SocketAikhess D): Writable RPC Serve Objeet Cdokenomdin call: Server Call) CiSse Client connecton className Base( in className: String): String al ClienT Call Writable in received Time: long): Writable iCak n call: Client Call): boolean Writable e认ork0:bokn xeadPunatin seadPanan: ClientCall) bwritFurprox veResponss0 e Proxy(in protocol: Class<>, in chen Version: long, in addr, in ticket, in conf, in factory HrespomseQueue: LinkedList-sCal serExoeprion( in error: IOExeeprion) p) leanmupcallso Client.ParallelS ude ry:SoekeFactary): Chent ullelResults lenin Contiguration): Chent Invoker netsocketAddress nnection: Server Comection et: UserGiroupnromanion VersionedPrntaced Hadoop源代码分析(六) 既然是RPC,自然就有客户端和服务器,当然,org, apache. hadoop.rpc也就有了类 Client和类 Server。在这里我们来仔细考 察 org. apache hadoop rpc. Client。卜面的图包含了org. apache. hadoop.rpc. Client中的关键类和关键方法。 由于 Client可能和多个 Server通信,典型的次HFS读,需婁和 Namenode打交道,也需要和某个/某些 Datanode通信。这 就意呔着某·个 Client需要维护多个连接。同时,为了诚少不必要的连接,现在 Client的儆法是拿 Connection(图中最右 侧)来做为 Connection的ID。 Connection包括一个 InetSocketaddress(I地址+端山号或主机名+端号)对象和一个用 户信息对象。这就是说,同一个用户到同一个 Inet socketAddress的通信将共享同一个连接 ava.lang Thread Client Conneetion nnections: HushtublecConnectonld connection> ueClass: Writable ncaLl in call): boolean ClientConnectinnId hunter: nt Isetup iOstream) -remoted nolean wait For Work(): boolean InetsocketAddres sendIng) ticket: UserGroupIntormatiol axIde Tire. int max Retries: int sendParamf in sendParam: Client Call) treceiveResponse() tFactory: SocketFactory is ZeroReferencef Client Call all(in param, in addr): Writable nl(in parms: Writable in addresses: InetSocket Address(D: Writable Hid: int IH(En param: Writable HcallCompleteO) HsctException( im error: IOException) Iset Value(in value Writable) Client. ParallelResults Client. ParallelCall -results index int e: in 连接被封装在类 Client. Connection中,所有的RPC调用,都是通过 Connection,进行通信。一个RPC调用,自然有输入参数 输出参数和可能的异常,同时,为了区分在同个 Connection上的不同调用,每个调用都有唯的id。调用是否结束也需要 个标记,所有的这些都体现在对象 Client.Cal1中。 Connection对象通过一个Hash表,维护在这个连接上的所有Call Java代码 1. private Hashtable<Integer, Call> calls new Hashtable<Integer call>( 一个NC调用通过 add calI,把请求加到 Connection里。为了能够在这个框架上传输Java的基本类型, String和 Writable接 口的实现类,以及元素为以上类型的数组,我们般把Ca11需要的参数打包成为0 bjectWritable对象。 Client. Connection会通过 socket连接服务器,连接成功后回校验客户端/服务器的版本号( Client. Connectionwriteheader 方法〕,校验成功后就可以通过 Writable对象来进行请求的发送/应答了。注意,每个 Client. Connection会起一个线程,不 断去读取 socket,并将收到的结果解包,找出对应的Ca1l,设置Ca11并通知结果已经获取 Cal使用0 be jct的wait和 notify,把RC上的异步消息交互转成同步调用 还有·点需要注意,个 Client会有多个 Client. Connection,这是个很自然的结果。 Hadoop源代码分析(七) 聊完了 Client聊 Server,按惯例,先把关图贴出来。 vaang Thread bruno) doRado Senver hindAddress; String IlQueue: Blocking< Call responder ct): server Ho AsyncWritcO etRemotelp): InetSocket Address I-handlers -pUrge( focessResponse( HdoRespond(in call: Server Call) dose connection( Handler Peall(in param: Writable, in receiveTime: long): Writable I-comectionList Server Connection hannel Socket( hannel Byte Butter response Queue: LinkedList<call> onnection) etHostAddresso param: Writable adandProves) mmection. Server Connection Kessheaderd cess Data() response: Byte Butter timedOut( 需要注意的是,这里的 Server类是个抽象类,唯一抽象的地方,就是 代码 1. public abstract Writable call(Writable param, long receive Time) throws lOExceptior 这表明, Server提供了一个架子, Server的具体功能,需要具休类来完成。而具休类,当然就是实现ca1l方法。 我们先来分析 Server.Call,和 Client.Cal1类似, Server.Ca包含了一次请求,其中,id和 param的含义和 Client.Cal 是·致的。不同点在后面三个属性, connection是该Call来自的连接,当然,当请求处理结柬吋,相应的结果会通过相同的 connection,发送给客户端。属性 times tamp是请求到达的时间戳,如果请求很长时间没被处理,对应的连接会被关闭,客户 端也就知道岀错了。最后的 response是请求处理的结果,可能是个 Writable的邙行化结果,也可能个异常的串行化结果。 Server. Connection维护了一个来之客户端的 socket连接。它处理版本校验,读取请求并把请求发送到请求处理线程,接收处 理结果并把结果发送给客户端 Hadoop的 Server采用∫Java的NO,这样的话就不需要为每一个 socket连接建立一个线程,读取 socket上的数据。在 Server 中,只需要一个线程,就可以 accept新的连接请求和读取 socket上:的数据,这个线程,就是上面图里的 Listener 请求处理线程·般有多个,它们都是 Server. Handle类的实例。它们的rmn方法循坏地取出个 Server.Ca11,调用 Server.call 方法,搜集结果并串行化,然后将结果放入 Responder队列中 对于处理完的请求,需要将结果写回去,同样,利用NI0,只需要个线程,相关的逻辑在 Responder里 Hadoop源代码分析(八) (注:本节需要用到·些Java反射的背景) 有了 Client和 Server,很自然就能Rr啦。下面轮到RPC.java啦。 殷来说,分布式对象一般都会要求根据接口生成存根和框架。如CORB,川以通过IDL,生成存根和框架。但是,在 org. apache. hadoop.rpc,我们就不需要这样的步骤了。上类图。 bindAddress: String dD): Server tgetRemotelp(: InetSocketAddress reconnection(y Ll in param: Writable, in receive Time: long): Writable RPC Server implementation: Classs> verbose: boolean <接口> Invocutionllandler BName Base( in className: String): String Feall( in param: Writable. in receivedTime: long): Writable RPO ddress: InetSocketAddress ticket: User Grouplnformation getProxy( in protocol Class< ?>, in client Version: long. in addr, in ticket, in conf, in factory ient. Client Proxy( imoe( (in proxy:C对 in method: Method, in args:Oq:O改 terver TENTS 楼口> org. apache hadoop. io: Writable RPCClientCache -clients: Hash Map<SocketFactory, Client RPCInvocation lien in conf, in factory SocketFactory): Client MopcEn in conf: Configuration): Client rumeterClasses: ClassI meters: ObjectD Font: Configuratio 为了分析 Invoker,我们需婁介绍些Java反射实现 Dynamic proxy的背景 Dy namic Proxy是由两个 class实现的:java.lang. reflect. Proxy和java.ang. reflect. Invocationhandler,后者是一个 接口。所谓 ynamic Proxy是这样一种 class:它是在运行时生成的 class,在生成它时你必须提供一组 interface给它,然后 该 class就宣称它实现了这些 interlace。 这个 Dynamic Proxy其实就是一个典型的 Proxy模式,它不会你作实质性的工作,在生成它的实例时你必须提供一个 handler, 出它接管实际的工作。这个 handler,在 Hadoop的RC中,就是 Invoker对象 我们可以简单地理解:就是你可以通过一个接口来生成一个类,这个类上的所有方法调用,都会传递到你生成类时传递的 Invocationhandler实现中 在 Hadoop的RC中, Invoker实现了 Invocationhandler的 invoke方法( invoke方法也是 Inyocationhandler的唯一方法)。 Invoker会把所有跟这次调用相关的调用方法名,参数类型列表,参数列表打包,然后利用前面我们分析过的 Client,通过 socket 传递到服务器端。就是说,你在 proxy类上的仁何调用,都通过 Client发送到远方的服务器上 Invoker使用 Invocation invocation封装了一个远程调用的所有相关信息,它的主要属性有: method Name,调用方法名 parameterClasses,调用方法参数的类型列表和 parameters,调用方法参数。注意,它实现了 Writable接凵,可以出行化。 RC. Server实现了org. apache. hadoop.ipc. Server,你可以把一个对象,通过KPC,升级成为一个服务器。服务器接收到的请 求(通过 Invocation),解串行化以后,就变成了方法名,方法参数列表和参数列表。利用Java反射,我们就可以调用对 的对象的方法。调用的结果通过 socket,返回给客户端,客户端把结果解包后,就可以返回给 Dynamic Proxy的使用者了 Hadoop源代码分析(九) 个典型的IS系统包括一个 Name Node和多个 DataNode. Name node维扩名宇空间;而Data\ode存储数据块。 Datanode负责存储数据,个数据块在多个 Data\ode中有备份:而个 DataNode对于个块最多只包含个备份。所以我们 可以简单地认为 DataNode上仔了数据块I)和数据块内容,以及他们的映射关系。 个IDS集群可能包含上千 DataNode节点,这些 DataNode定时和Name丶ode通信,接受 Name Node的指令。为了减轮 NameNode 的负担, NameNode上并不水久保存那个 DataNode上有那些数据块的信息,而是通过 DataNode启动时的上报,来更新 Namenode 上的映射表。 Datanode和 NameNode建立连接以后,就会不断地和 Namenode侏持心跳。心跳的返回其还也包含了 Namenode对 Datanode的 些命令,如删除数据斥或者是把数据块复制到另一个 Data\ode。应该注意的是: Namenode不会发起到 Data node的请求,在这 个通信过程中,它们是严格的客户端/服务器架构 Datanode当然也作为服务器接受来自客户端的访问,处理数据块读/写请求。 Da ta node之间还会相互通信,执行数据块复制任 务,同时,在客户端徹写操作的吋候, Datanode需要相互配合,保证写操作的致性。 下面我们就来具体分析下 DataNode的实现。 DataNode的实现包括两部分,·部分是对本地数据块的管理,另部分,就是 和其他的实体打交迨。我们先来看本地数据块管理部分 安装 Hadoop的吋候,我们会指定对应的数据块存放日录,当我们检查数据块存放日录日录时,我们回发现下面有个叫dfs的日 录,所有的数据就存放在ds/data里面。 Name Date 2008-11-2715:4 Detach 20-11-1410:28:00 gtmp 208-11-2715:41:00 回 in use. lock 2006-11-1410:28:00 2008-11-1410:28:00 其中有两个文件, storage里存的东西是一些出错信息,貌似是版本不对…云云。 In use.lock是一个空文件,它的作用是如果 斋奘对整个系统做排斥操作,应用应该获取它上面的个锁。 接卜来是3个日录, current存的是当前有效的数据块, detach存的是快照( snapshot,目前没有实现),tmp保存的是一些 操作要的临时数据块 但我们进λ currenT日录以后,就会发现有一系列的数据块文件和数据块元数据文件。同时还有一些子日录,它们的名字是 ubdir0到 subdir63,」日录下也有数据块文件和数据块元数据。这是因为HDFS限定了每个日录存放数据块文件的数量,多了 以后会创建子目录来保仔。 数据块文件显然保存了IDFS中的数据,数据块最大可以到6M。每个数据块文件都会有对应的数据块元数据文件。里面存放的 是数据块的校验信息。下面是数据块文件名和它的元数据文件名的例了 blk3118782637964391313 blk3148782637964391313242812.meta 上面的例子中,3148782637964391313是数据块的ID号,242812是数据块的版本号,用于一致性检查 在 current目录下还有下面几个文件: ERSION,保存了些文件系统的元信息 dncp block verification.log.cur和 dncp block verification.log.prev,它记录了一些 Datanode对文件系定时统做一致 性检查需要的信息。 Hadoop源代码分析(一零) 在继续分析 Datanode之前,我们有必要看下系统的工作状态。启动HDFS的时候,我们可以选择以卜启动参数: FORMAT(" -format")}:格式化系统 REGULAR(" regular"):正常启动 PGRADE("- upgrade"):升级 ck"):回滚 FINALIZE("- finalize"}:挺交 IMPORT(" mport Checkpoint"):从 Checkpoint恢复。 作为一个大型的分布式系统Hadoop内部实现了一套升级机制(http://wiki.apacheorg/hadoop/hadoopUpgrade)oupgrade 参数就是为了这个目的而存在的,当然,升级可能成功,也可能失败。如果失败了,那就用 rollback进行回滚;如果过了段 时间,系统运行正常,那就可以通过 finalize,正式提交这次升缴(跟数据库有点像啊) i mport Checkpoint选项用于Ⅴ ame node发生改障后,从某个检查恢复。 有了上面的描述,我们得到下面左边的状态图 NORMAL NORMAL update i rollback /finalize ROLLBACKING UPGRADING IFINALIZING H UPGRADED / rollback finalize UPGRADED 大家应该注意到,上面的升级/回滚/提交都不可能·下就搞定,就是说,系统故障时,它可能处于上面右边状态中的某·个 特别是分布式的各个节点上,甚至可能出现某些节点己经升级成功,但有些节点可能处于中间状态的情况,所以 Hadoop采用类 似于数据库事务的升级机制也就不是很奇怪。 大家先理解一下上面的状态图,它是下面我们要介绍Data、ode储的基础: Hadoop源代码分析(一一) 我们来看下升级/回滚/提交时的 Datanode上会发生什么(在类 Datastorage中实现) 前面我们提到过VRSI0N文件,它保存了一些文件系统的元信息,这个文件在系统升级时,会发生对应的变化 升级时, Namenode会将新的版本号,通过 Datanode的登录应答返回。 Datanode收到以后,会将当前的数据块文件目录改名, 从 current改名为 previous.tmp,建立一个 snapsho,然后重建 current录。重建包括重建WRSI0N文件,重建对应的子日 录,然后建立数据块文件和数据块元数据文件到 previous.tm的硬连接。建京崨连接意味着冇系统中以侏留份数据块文件和 数据块元数据文件, current和 previous.tmp中的相应文件,在存储中,只保留一份。当所有的这些工作完成以后,会在 current 里写入新的VRSI0N文件,并将 previous.tmp目录改名为 previous,完成升级 了觖了升级的过程以后,回滚就相对简单。因为说有的旧版本信息都保存在 previous日录里。回滚首先将 curreηt日录改名为 removed.tmp,然后将 previous目录改名为 current,最后除 removed.tm目录。 提交的过程,就是将上面的 previous日录改名为 finalized.tmp,然后启动个线程,将该日录删除 下图给出」上面的过程: NORMAL 1. current->removed. tmp I. current->previous, tmp Finalize 2. previous->current 2.重建 current 1. previous->finalized, tmp 3.删除 removed. tmpt 3. previous, tmp->previous 2.删除 finalized, tmp UPGRADED 需要注意的是,HDFS的升级,往往只是支持从某个特点的老版本升级到当前版本。回滚吋能够恢复到的版本,也是 previous 中记录的版本。 下面我们继续分析 Datanode 文字分析完 DataNode仔储在文件上的数据以后,我们来看一下运行时对应的数据结构。从大到小, Hadoop中最大的结构是 Storage,最小的结构,在 DataNode上是 block Storage保仔了和存储相关的信息,它继承了 Storage info,应用于 DataNode的 Datastorage,则继承了 Storage,总体类 图如下 Storagelnfo namespace: int Storage Storage Directory ock: File Lock lirType: Storage Storage DirType Stomge storage Type: enumeration -storage Dirs read(in from: File Iterator: Iterator earDirectoryo Fget Din(): Storage Starage Directory analyze Storage(in startOpt: enumeration): enumeration laduStorage Dir(in sd: Storage Storage Directory) Recover in curtate: enumeration) getField in props. in sd loc kD Fields(in props, in sd) mein from, in to amen Pdelete Dir( in dir) orruptPreUpgradeStora .writeAble His Conversion Needed (in sd): boolean jOcko FisLock Supported(in idx) <接口> tgelStorugeDir-Type(: Storage Storage DirTipe Datastorage -storaged: String Datastore( wer TransitionRead(in nsinfo, in dataDirs: Collection<File>, in startOpt: enumeration) format(in sd. in nsInfo: NamespuceInto) HdoTransition(in sd. in nsInto, in startor) doLpgrude(in sd, in siNto doRollback( in sd. in nsinfo) doFinalize(in sd) tinalizeUpgradeo link Blocks(in from: File, in to, in old v: int) rruptPreUpgrade in rootDir) Verify DistributelUpgrade Progress(in nsInfo) torage Info包含了3个子段,分别是 lavoutversion:版本号,如果 Hadoop调整文件结构布局,版本号就会修改,这样可以 保证文件结构和应用一致。 namespace是 Storage的I), cTime, creation t ime 和 StorageInfo相比, Storage就是个大家伙了。 torage可以包含多个根(参考配置项dfs.data.dir的说明),这些根通过 Storage的内部类 StorageDirectory来表示 StorageDirectory中最重要的方法是 analyzestorage,它将根据系统启动时的参数和我们上面提到的一些判断条件,返回系统 现在的状态。 StorageDirectory可能处于以下的某个状态(与系统的工作状态·定的对应) NON EXISTENT:指定的日录不存在 NOT FORMATTED:抬定的目录存在但未被格式化 COMPLETE UPGRADE: previous tmp存在, current也存在 RECOVER UP(RADF: previous tmp存在, currcnt不存在 COMPLETE FINALIZE: finalized. tmp存在, current也存在 COMPLETE ROLLBACK: rcmoved tImp存, current也存在, prcvIous不存代 RECOVER ROLLBACK: removed. tmp存在, cuirrent不存在, previous存在 COMPLETE CHECKPOINT: lastcheckpoint lmp存在, current也存在 RECOⅤ ER CHECKPOINT: lastcheckpoint. tmp存在, current不存在 NORMAL:普通L作模式 StorageDirectory处于某些状态是通过发生对应状态改变需要的工作文件夹和正常工作的 current夹来进行判断。状态改变需 要的工作文件火包括 previouIS:用丁升级后保存以前版木的文件 prcvIous. tmp:用于升级过程中侏存以前版本的文件 removed,tmp:用丁回滚过程中保存文件 finalized. tImp:用于提交过程中保存文件 lastcheckpoint LImp:应用于从 Name Node中,导入一个检查点 previous checkpoint:应用于从 NamcNodo中,结束导入一个检查点 有了这些状态,就可以对系统进行恢复(逦过方法 reCover)。恢复的动作如下(结合上而的状态转移图): COMPLETE UPGRADE: my previous, tmp-> previous RECOVER UPGRADE mv previous. tmp ->current COMPLETE FINALIZE: rm finalized. tmp MPLETE ROLLBACK: rm removed. tmp) RECOVER ROLLBACK: my removed. tmp -> current COMPLETE CHECKPOINT: mv lastcheckpoint tmp - previous checkpoint RECOVER CHECKPOINT: mv lastcheckpoint. tmp-> current 我们以 RECOVER UPGRADE为例,分析一下。根据升级的过程, 2.重建 current prcvious. tmp- 当我们发现 previous.tmp存在, current不存在,我们知道只需要将 previous.tm改为 current,就能恢复到未升级时的状态 StorageDirectory还管理着文件系统的元信息,就是我们上面提过 StorageInfo信息,当然, StorageDirectory还保存每个具 体用途自己的信息。这些信息,其实都仔储在WKSl0N文件中, StorageDirectory中的read/ write方法,就是用于对这个文 件进行读/写。下面是某一个 DataNode的 VERSION文件的例子: 酉置文件代码 1.# Fri nov1410:27:35CST2008 2. namespacelD=1950997968 3. storaged=Ds697414267-127.0.0.1-50010-1226629655026 5. storage Type=DATA NODE 6. layoutVersion=-16 对 StorayeDirec'tory的排他操作需要锁,还记得我们在分析系统∏录时提到的 In use.lock文件吗?它就是用来给整个系统加 解锁用的。 StorageDirectory提供了对应的10ck和 unlock方法 分析完 StorageDirectory以后, Storage类就很简单∫。基本上都是对一系列 StorageDircctory的操作,同时 Storage提供 一些辅助方法。 Datastorage是 Storage的了类,专门应用于 Datanode上面我们对 Datanode的升级/回滚/提交过程,就是对 Datastorage的 upgrade/ doRollback/ dofinalize分析得到的。 DataStorage提供了 format方法,用于创建 Datanode上的 Storage,同时,利用 StorageDirectory, Datastorage管理存储系 统的状态。 Hadoop源代码分析(一二) 分析完 Storage相关的类以后,我们来看下一个大家伙, DAtaset相关的类

...展开详情
试读 83P Hadoop源代码分析(完整版).pdf
立即下载
限时抽奖 低至0.43元/次
身份认证后 购VIP低至7折
一个资源只可评论一次,评论内容不能少于5个字
您会向同学/朋友/同事推荐我们的CSDN下载吗?
谢谢参与!您的真实评价是我们改进的动力~
  • GitHub

  • 分享王者

关注 私信
上传资源赚钱or赚积分
最新推荐
Hadoop源代码分析(完整版).pdf 16积分/C币 立即下载
1/83
Hadoop源代码分析(完整版).pdf第1页
Hadoop源代码分析(完整版).pdf第2页
Hadoop源代码分析(完整版).pdf第3页
Hadoop源代码分析(完整版).pdf第4页
Hadoop源代码分析(完整版).pdf第5页
Hadoop源代码分析(完整版).pdf第6页
Hadoop源代码分析(完整版).pdf第7页
Hadoop源代码分析(完整版).pdf第8页
Hadoop源代码分析(完整版).pdf第9页
Hadoop源代码分析(完整版).pdf第10页
Hadoop源代码分析(完整版).pdf第11页
Hadoop源代码分析(完整版).pdf第12页
Hadoop源代码分析(完整版).pdf第13页
Hadoop源代码分析(完整版).pdf第14页
Hadoop源代码分析(完整版).pdf第15页
Hadoop源代码分析(完整版).pdf第16页
Hadoop源代码分析(完整版).pdf第17页

试读结束, 可继续读3页

16积分/C币 立即下载