Hadoop源代码分析完整版

所需积分/C币:10 2013-09-26 08:42:37 6.06MB PDF
9
收藏 收藏
举报

Hadoop源代码分析完整版,非常全面的hadoop源码解析
这里,我把0 b.able标为红色,是因为相对于其他对象,它有不同的地位。当我们讨论 Hadoop的RC时,我们会提到 RPC上交换的信息,必须是Java的基本类型, String和 Write1e接口的实现类,以及元素为以上类型的数组 服务器间传输的 Writable对象。例如,我们要把上面例子中的对象作为RPC请求,需要根据 Writable创建于客户端/ Ob jec: wRitable对象保存了一个可以在RPC上传输的对象和对象的类型信息、。这样,我们就有了一个万能的,可以用 Ob jec WRitable, Ob jeclWritable往流里会写如下信息 对象类名长度,村象类名,对象自已的点行化结果 这样,到∫对端, Ob jeclWritable可以根据村象类名创建对应的对象,并解串行。应该注意到, ObjectWrilable依赖于 Writablefactories,那存储了 Writable子类对应的工厂。我们需要把 MyWritable的工厂,保存在 WritableFactories中(通 if WritableFac tories setFaciory) Hadoop源代码分析(五) 介绍完org. apache. hadoop.io以后,我们开始来分析org. apache. hadoop.rpc。RPC采用客户机/服务器模式。请求程序就是 个客户机,而服务提供程序就是一个服务器。当我们讨论HDFS的,通信可能发生在: Client-NameNode之间,其中 NameNode是服务器 Client-Data Node之间,其中 DataNode是服务器 Data Node- Namenode之间,其中 NameNode是服务器 Data Node- Date Node之间,共中某一个 Date Node是服务器,另一个是客户端 如果我们考虑 Hadoop的Map/ Reduce以后,这些系统闩的通信就更复杂了。为了解决这些客户机/服务器之间的通信, Hadoop引 入」一个R℃柱架。该RC框架利用的Jwa的反射能力,避免∫某些RC解决方案中需要根据某种接口语言(如 CORBA的 IDL)生成存根和框架的问题。但是,该RPC框架要求调用的参数和返回结果必须是Jaa的基本类型, String和 Writable接 的实现类,以及元素为以上类型的数组。同时,接口方法应该只抛出 IOExceplion异常。(参考白 既然是RPC,当然就有客户端和服务器,当然,org. apache. hadoop.rpc也就有了类 Client和类 Server。但是类 Server是 抽象类,类RPC埘装了 Server,利用反射,把某个对象的方法廾放出来,变成RPC中的服务器 Writable Canligurab+: TweDAITaa wrtahietemparahie GerereYntobk writahefnmjarat Arrastre AbanetAop ll'n'tblo Ikeanwrtabkyewriabl MDSIlash apw rita:StrudM-pwria Flaatwntab r sngWrtaale NulwriableVIntWritabl:VLongwritable ssPdw'ntasd Dories WriaaNclactery Primary TyPee ArrwⅤFik CFIA DatsliparDaiffe strknglller 下图是org. apache. hadoop.rpc的类图。 ----mu + UsAna Isn DNDDxla: boa ea ke: BlOck知uu== cketFatory S.xckEtFatcry rmote p(: Inetsucse crees Hhand eTE tsall in aram hri bk[, in adham: LactSoEwcthvoll WrasbkD +perC region) Clenecnrzeton asNa me Race( a d aR Nane: Siring) Stis tateh fall;.w-its sle,inreeiner'i ICaI(iI tall Clead Call). bleat waitlErWorto balcan r:tN甲 Cerf- mn(n senrParmrm Cl mt C. l) k anr Socket, ann 下 IKEWaus'in malus: W b/i j 1 remoted -clIentS FovessDalid Client. ParallalCall-rsuhs Uie:rcuplnknnticn Wasat inLander 己8 ScaksFsztory': O server cell tera In cC.* orig irarior): clr pier n c ient: client rss:【m:5 MeketAddirs5 HCuiacuiua. Seici Cuucitiun responE byte rlr inl helan cpw:ch国M减g:(:c dPrstoool Wie l y .conf: Contigumntin Hadoop源代码分析(六) 既然是RPC,自然就有客户端和服务器,当然,org. apache. hadoop.rpc也就有了类 Client和类 Server。在这里我们来仔细考 察 org. apache. hadoop.rpc. Client。下面的图包含了 org. apache. hadoop. rpc. Client中的关键类和关键方法 由于 Client可能和多个 Server通信,典型的一次HS读,需要和№ amende打交道,也崙要和某个/某些 Datanode通信:这就 意味着某一个 Client需要维护多个连接。同时,为了减少不必要的连接,现在 Client的做法是拿 Connection(图中最右 侧)来做为 Connection的ID。 Connection包括一个 Ine socke address(IP地址+端口号或主机名+端口号)对象和一个用 户信息对象。这就是说,同一个用户到同一个 Inet SocketAddress的通信将共享同一个连接。 java. lang Threa Client conneetion sonnei:ns: HiHshtubjesconmecrisnld Connectiun-a va ueciasz writable cCall( in call: boolean unter: -remoted rurning: boolean rForkork): boolean ddess: lietsoiket jdress -ticket: UicrGroupIntormat rldet'm. rt Param in sendpuram: Client Call) p OdElay boolean geNre val int ketFactory: SocketFactory eroReere ClientcAll d jim: Writable hre: Y-itmbl e Exception(in errr: IOEnceptin) Ise Yalue(in value: Writable) Client, ParmllelCall -esults I count: int 连接被封装在类 Client. Connection中,所有的RPC调用,都是通过 Connec uion,进行通信。一个RPC调用,白然有输入参数, 输出参数和可能的异常,同时,为了区分在同一个 Connection上的不同调用,每个调用都有唯一的id。调用是否结束也需要 个标记,所有的这些都体现在对象 Client..Call中。 Conneetion对象通过一个Hash表,维护在这个连接上的所有Call Jaa代码 1. private Hashtable<Integer call> calls =new Hashtable<Integer, Call>0 个RPC调用通过 adca.l1,把请求加到 Connection里。为了能够在这个框架上传输Java的基本类型, String和 Writable接 口的实现类,以及元素为以上类型的数组,我们一般把Ca11需要的参数打包成为0 bjectWritable对象。 〔 Lient. Cornection会通过 socket连接服务器,连接成功后回校验客户端/服务器的版木号 ( Client. ConnectionwriteHeader o方法),校验成功后就可以通过 Writable对象来进行请求的发送/应答了。注意,每个 Client. Connection会起一个线程,不断士读取 socke,并将收到的结果解包,找出对应的Cal1,设冒Call并通知结果已经 获取。 Cal1使用0ejeu的wait和 notify,把RPC上的异步消息交互转成同步调用 还有一点需要注总,一个 Clien会有多个 Client. Conmection,这是一个很白然的结果 Hadoop源代码分析(七) 聊完了 Client聊 server,按惯例,先把类图贴出来。 java lang Thread trur listener stop -hindAddress: String Responder tallQueue: Blocking QueuecCall> responde etRemotelE): InetSocket Address loseConmect andler lt(in pram: Writable, in receive Time: long): WritabLe Server conneetion respunse Queue:LinkedList<call onnect on param: Writable readAIPruvessy connEction: Seryer Curtiz cessheadero timestamp: long ToccssDatad) esponse:ByteBuffer medO) 需要注意的是,这里的 Server类是个抽象类,唯一抽象的地方,就是 」ava代码 1. public abstract Writable call (Writable param, long receive Time)throws IO Exce ption 这表明, Server提供了一个架子, Seryer的具体功能,需要具体类来完成。而具体类,当然就是实现ca1l方法。 我们先来分析 Server.Ca1l,和 Client.Ca11类似, Server.Ca1l包含了一次请求,其中,id和 param的含义和 Client.Call 是一孜的。不同点在后面三个属性, connection是该Call来白的连接,当然,当请求处理结束时,相应的结果会通过相同的 connection,发送给客户端。属性 lines lan是请求到达的时间戳,如果请求很长时间没被处理,对应的连接会被关闭,客户 端也就知道出错了。最后的 response是请求处理的结果,可能是一个 Writable的韦行化结果,也可能一个异常的串行化结果 Server. Connectio维护了一个来之客户端的 socket连接。它处理版本校验,读取请求并把请求发送到请求处理线程,接收处 理结果并把结果发送给客户端。 Hadoop的 Server采用了Java的、IO,这样的话就不需要为每一个 socket连按建立一个线程,读取 socket上的数据。在 Server中,只需要一个线程,就可以 accept新的连接请求和读取 socket上的数据,这个线程,就是上面图里的 Listener A,cal)法,搜集结果并串行化,然后将结果放入 ResponderS% 请求处理线程一般有多个,它们都是 Server. Handle类的实例。它们的run方法循环地取出一个 Server.Cal1,调用 对于处理完的请求,需要将结果写回去,同样,利用NIO,只需要一个线程,桕关的逻辑在 Responder里 Hadoop源代码分析(八) (注:本节需要用到一些Java反射的背景) 有了 Client和 Server,很目然就能RPC啦。下面轮到RPC.java啦。 般来说,分布式对象一般都会求根据接凵生成存根和框架。如 CORBA,可以通过IDL,生成存根和框架。但是,在 rg. apache hadoop.rpc,我们就不需要这样的步骤了。上类图 rirxlAeldress rring cal ICaue: Blocking Qweuetall> et: Server IgetRemotelp(): InetSxckeLAddress mm Writable, in receive Tire: long ) Writable RPC Server mplementation: classes nvoentionHlandler in className: String): Strins Writable, in receivedT me: long): Writable InvoKer Class<?>, n client Version: long. in ad, in ticket in uorf, in factory] Clent H-iscIosed boolean invoke(in proxy:O为hed:ehn些:Oe:et II-CLIUNT5 <[ >>ur apache hEop io Writ abl RPC Clesrcach Clients: Has<socket=actory. client> EPCInvoreatian tt entin oon, n tactors Sock: Factcry): Client I.ien(in conf: Configration):Client 为了分析 Invoker,我们需要介绍一些Java反射实现 Dynamic Proxy的背景。 Dynamic Proxy是由两个 class实现的:java.lang. reflect. Proxy和java.lang. reflect. Invocationhandler,后者是一个 接口。所诣 Dynamic Proxy是这样一种 class:它是在运行时生成的 class,在生成它时你必须提供一组 inTerlace给它,然后 该 class就宜称它实现了这 这个 Dynamic Proxy其实就是一个典型的 Proxy模式,它不会替你作实质性的工作,在生成它的实例时你必须提供一个 handler,出它接管实际的工作。这个 handler,在 Hadoop的RPC中,就是 Invoker对象 我们可以简单地理解:就是你可以通过一个接口来生成一个类,这个类上的所有方法调用,都会传递到你生成类时传递的 实现中 在 Hadoop的RPC中, Invoker实现了 Invocationhandler的 invoke方法( invoke方法也是 Invocationhandler的唯一方法) Invoker会把所有跟这次调用相关的调用方法名,参数类型列表,参数列表打包,然后利用前面我们分析过的 Client,通过 socket传递到服务器端。就是说,你在 proxy类上的仁何调用,都通过 Client发送到远方的服务器上。 Invoker使用 Invocation: Invocation封装了一个远程调用的所有相关信息,它的主要属性有: me thod\ae,调用方法名, parameterClasses,调用方法参数的类型列表和 parameters,调用方法参数。注意,它实现了 Writable接口,可以串行化。 C. Server实现了org. apache. hadoop.ipc. Server,你可以把一个对象,通过RPC,升级成为一个服务器。服务器接收到的请 求(通过 Invocation),解串行化以后,就变成了方法名,方法参数列表和参数列表。利用Java反射,我们就可以调用对应的 对象的方法。词用的结果再通过 socke,返回给客户端,客户端把结果解包后,就可以返回给 Dynamic proxy的使用者了 Hado0p源代码分析(九) 个典型的FS系统包括一个№ amenDe和多个 Datanode,\ amende维护名字空问:而 Data\ode存储数据块 Data\ode负责存储数据,一个数据块在多个 Datanode中有备份:而一个 Da ta node对于一个块最多只包含一个备份。所以我们 可以简单地认为 Datanode上存」数据块ID和数据块内容,以及他们的映射关系 个田S集群可能包含上千 Palinode节点,这些 Dalanode定时和 Namenode通信,接受 Namenode的指令。为∫减轻 amende 的负担, NameNode上并不永久保存那个 DataNode上有那些数据块的信息,而是通过 DataNode启动时的上报,来更新 Nane \ode上的映射表 Data\ode和№ ameNde建立迕接以后,就会不断地和 Namenode保持心跳。心跳的返回其还也包含了 Name ode对 DataNode的 些命令,如删除数据厍或者是把数据块复制刭力一个 DataNode。应该注意的是: Name node不会发起到Data\ode的请求,在这 个通信过程中,它们是严格的客户端/服务器架构 nata\ode当然也作为服务器接受来自客户端的访问,处理数据块读/写请求。 Datanode之间还会相互通信,执行数据块复制任 务,同时,在客户端做写操作的时候, Datanode需要相互配合,保证写操作的一致性。 下面我们就来只体分析一下 DataNode的实现。 DataNode的实现包括两部分,一部分是对本地数据块的管理,另一部分,就是和 其他的实体打交道。我们先来看本地数据块管理部分 安装 Hadoop的时候,我们会指定对应的数据块存放目录,当我们检査数据块存放目录目录时,我们回发现下面有个叫dfs的 日录,所有的数据就存放在dfs/data里面 28-11-1410:28:00 2008-11-2715:41:00 0 200-11-141:28:00 trade 2U8-11-1410:28:00 共中有两个文件, storage里存的东西是一些出错信息,貌似是版本不对…云云。 In use.lock是一个空文件,它的作用是如果 需要对整个系统做排斥操作,应用应该获取它上亩的一个锁。 接下来是3个目示, current存的是当前有效的数据块, detach存的是快照( snapshot,目前没有实现),tm保存的是一些 操作需要的临时数据块。 但我们进入 current日录以后,就会发现系列的数据块文件和数据块元数据文件。同时还有一些子日录,它们的名子是 cubdir0到 subdir63,子目录下也冇数据块文件和数据块元数据。这是因为HDFS限定了每个目录存放数据块文件的数量,多了 以后会创建子日录来保存。 数据块辶件显然保存了HFS中的数据,数据块最大可以到64M每个数据块文件都会有对应的数据块元数据文件。里面存放的是 数据块的校验信息。下面是数据块文件名和它的元数据文件名的例子 blk3148782637964391313 blk3l18782637961391313212812.meta 上面的例子中,3148782637964391313是数据块的ID号,242812是数据块的版本号,用于一致性检查。 在 current目录下还有下面儿个文件 VERSI0N,保存了一些文件系统的元信息 dep block verification.log.curr和 dnep block verificaTion.log.prev,它记录了一些 Datanode对文件系定时统做一致 检查需要的信息。 Hdoo源代码分析(一零) 在继续分析 Da Anode之前,我们有必要看一下系统的工作状态。启动DFS的时候,我们可以选择以下启动参数 FORMAT(" format"):格式化系统 REGULAR(" regular"):常启动 GRADE(" upgrade"):开级 ROLLBACK(" rollback"):回滚 FINALIZE("fna|ize")}:提交 MPORT("- import Checkpoint"):从 Checkpoint恢复、 作为一个大型的分布式系统, Hadoop内部实现了一套升级机制 htD://wiki, apache,org/ hadoop./ Hadoop Upgrade) upgrade参数就是为了这个目的而存在的,当然,升级可能成功,也 可能失败。如果失败了,那就用 rollback进行回滚;如果过了一段时间,系统运行忙常,那就可以通过 finalize,正式提交这 次升级(跟数据库有点像啊)。 i Checkpoint选项用于 Nane node发生故障后,从某个检查点恢复。 有」上面的描述,我们得到下亩左边的状态图 NORMAL NORMAL update / update /finalize ROLLBACKING UPGRADING FINALIZING UPGRADED resllbuek UPGRADED 大家应该注意到,上面的升级/囯滚/提交都不可能一·下就搞定,就是说,系统故障吋,它可能处于上面右边状态中的某·个 特别是分布式的各个节点上,甚至可能出现某些节点已经升级成功,但有些节点可能处于中间状态的情况,所以 Hadoop采用 类似于数据庳事务的升缴机制也就不是很奇怪 大家先理解一下上面的状态图,亡是下面我们要介绍 Datanode存储的基础。 Hadoop源代码分析(-一) 我们来看一下升级/回滚/提交时的 DataNode上会发生什么(在类 DataStorage中实现) 前面我们提到过 VERSION文件,它保存了一些文件系统的元信息,这个文件在系统升级时,会发生对应的变化。 升级时, NarneNode会将新的版本号,通过 Datanode的登求应答返回。 Data\ode收到以后,会将当前的数据块文件目求改名, 从 current改名为 previous.tmp,建讠一…个 snapshot,然后重建 current日录。重建包括重建 VERSION文件,重建对应的子日 录,然后建立数据块文件和数据块元数据文件到 previous.tm的硬连接。建立硬连接意味着在系统中只保留一份数据块文件和 数据块元数据文件, current和 prevIous.tm中的相应文件,在存储中,只保留‘份。当所仃的这些工作完成以后,会在 urrent里写入新的 VERSI0N文件,并将 previous.tmp目录改名为 previous,完成升级 解了升级的过程以后,回滚就相对简单。因为说冇的旧版本信息都保存在 previous目录里。回滚首先将 current目录改名为 emoved.mp,然后将 previous录改名为 current,最后删除 removed.m求 提交的过程,就是将上面的 previous目录改名为 finalized.m,然后启动一个线程,将该目录删除。 下图给出了上血的过程: NORMAL i rollback I, current->removed. tmp 1. current- previous tmp 2里建 L. peeve 3.删除 removed. tmp 3. previous. tmp->previou 2.删除 finalized, tmp UPGRADED 需要注意的是,HDFS的升级,往往只是支持从某一个特点的老版本升级到当前版本。回滚时能够恢复到的版本,也是 previous 中记录的版本。 下面我们继续分析 Datanode 文字分析完 DataNode存储在文件上的数据以后,我们来看一下运行时对应的数据结构。从大到小, Hadoop中最大的结构是 Storage,最小的结构,在 Dat anode上是 block 类 Storage保存了和存储相关的信息,它继承了 StorageInfo,应用于 DataNode的 Datastorage,则继承了 Storage,总体类 图如下 layoutVersion: int mespacelD: int Storage. storageDirectorv root: File irT'ype Storage Storage Dir Type Storge storage Type: enurnerulion storage Dirs Fread(in fron:Filey write(in to: File) icarDirectory o ageDin malyze Storage[in startOpt: enumeration): enumeration adStoragre Din(in sd Storage StuTuge Directory ecover( in curTate: enumeration geLFiclds(an prEps, in sd) lockO setFields in props, in sd) trom, in to) leteDirf in dirl arruptPreUpgracs StoTHgEO IwriteAlO version Needed( in su): boolean OCALA cLock Supported( in idx) FgetRegistrationl DO <接口> Storage torage Dir'Typc get Storage Dir Type(: Storage: SMuruge Di pe +iser Tyne(in tine. Storage. SorrageDiP T)pre): barifeah elD: strin +Dat sToraged erlransition Read in nsInfo, in data Dirs: Collectior<File>. in star Opt: enumeration) cmat(in sd, in nsLnfo: NarmespaceInto) onversion. sd: boolean doTransitiondin sd, in nsInto, in startOpt) doL pgrade(in sd, in siNto) cio Rollback( in sd. in nslnfo) tinalizeupgradenl link Blocksin trom File in to, in old. V: inth orrupt PreUpgrade Storngein rootDir venilyDistnbutedUpgrudePrugress(in niNfa StorageInω包含了3个宇段,分别是 layout Version:版木号,如果 Hadoop调整文件结构布局,版本号就会修改,这样可以 保证文件结构和应用致。 namespace是 Storage的ID, cTime, creation time。 和 Storagelnfo相比, Storage就是个大家伙了 Storage可以包含多个根(参考配置项dfs.data.dir的说明),这些根通过 Storage的内部类 StorageDirectory来表小。 SLorageDirec tory中最重要的方法是 analyze Storage,它将根据系统启动时的参数和我们上面提到的一些判断条件,返回系 统现在的状态。 StorageDirectory可能处于以下的某一个状态(与系统的工作状态一定的对应) NON EXISTENT:指定的日承不存在 NOI上 ORMATTED:指定的目录存在但未被格式化 COMPLETE UPGRADE: previous. tmp存在, curren也存在 RECOⅤ ER UPGRADE: previous tmp存在, current不存在 COMPLETE FINALIZF: finalized. tmp存在, currcnt也存在 COMPLETE ROLLBACK: removed. tmp存在, current也存在, previous不存在 RECOⅤ ER ROLLBACK: removed. Lmp存, current不存在, previous存 COMPLETE CHECK POINT: astcheckpoint. tmp存在, current也存在 RECOVER CHECKPOINT: lastchcckpoint. tmp存在, current不存在 NORMAL:普通⊥作模式。 StorageDirectory处于某些状态是通过发生对应状态改变需要的工作文件夹和正常工作的 current夹来进行判断。状态改变崙 要的工作文件夹包括 previous:用于J级后保仔以前版本的文件 previous tmp:用于升级过程中保存以前版本的文件 removed. tmp:用于回滚过程中保文件 finalized. tmp:用于提交过程中保存文 应用于从 Namcnodo中,导入·个检查点 previous checkpoint:应用从 NameNode中,结束导入一个检查点 有了这些状态,就可以对系统进行恢复(通过方法 doRecover)。恢复的动作如下(结合上面的状态转移图) COMPLETE UPGRADE: mv previous tmp -> previous RECOVER UPGRADE: mv previous tmp ->current COMPLETE FINALIZE: rm finalized. ump COMPLETE ROLLBACK: rm removed. tmp RECOVER ROLLBACK: llv removed. lmp-> current COMPLETE CHECKPOINT mv lastcheckpoint tmp-> previous checkpoint RECOVER CHECKPOINT: mlv las checkpoint. ump->current 我们以RECOⅤ ER UPGRADE为例,分析一卜。根据升级的过程 I. current->previous tmp 2.旦建 current 3. previous tmp ->previous 当我们发现 previous.tmp存在, current不存在,我们知道只崙要将 previous.tm改为 current,就能恢复到未升级吋的状 态 Storagedirectory还管理着文件系统的元信息,就是我们上面提过 Storage info倍息,当然, StorageDirectory还保存每个 具体用途白己的信息。这些信息,其实都存储在VERS10\文件中, StorageDirectory中的read/ write方法,就是用于对这个文 件进行读/写。下面是某一个 DataNode的WRSI0N文件的例子 n置文件代吗 1.# Fri noy1410:27:35CST2008 2. namespace=1950997968 3. storageD=Ds-697414267-127.0.0.1-50010-1226629655026 4. cTime=0 5. storage Type=DATA NODE 6. layoutVersion=-16 对 SLoragedirectory的排他操作需要锁,还记得我们在分析系统目录时提到的 i use.lock文件吗?它就是用来给整个系统 加/解锁用的。 StorageDirectory提供了对应的1ock和 unlock方法。 分析完 StorageDirec tory以后, Storage类就很简单了。基木上都是对一系列 StorageDirectory的操作,同时 Storage提供 当辅助方法 DataStorage是 Storage的子类,专门应用于 Datanode上亩我们对 Datanode的升级/回滚/提交过程,就是对 Datastorage的 topgrade/ doRollback/ finalize分析得到的 alaslorage提供 r formal方法,用于创建 Datanode上的 Storage,同时,利用 SLorageDirectory, Dilaslorage管理存储系 统的状态。 Hadoop源代码分析(一二) 分析完 Storage相关的类以后,我们来看下一个人家伙, FSDa lase相关的类 上面介绍 Storage时,我们并没有涉及到数据块 Block的操作,所有和数据块相关的操作,都在 FSDataset相关的类中进行处 理。下面是类图:

...展开详情
试读 82P Hadoop源代码分析完整版
立即下载
限时抽奖 低至0.43元/次
身份认证后 购VIP低至7折
一个资源只可评论一次,评论内容不能少于5个字
您会向同学/朋友/同事推荐我们的CSDN下载吗?
谢谢参与!您的真实评价是我们改进的动力~
关注 私信
上传资源赚钱or赚积分
最新推荐
Hadoop源代码分析完整版 10积分/C币 立即下载
1/82
Hadoop源代码分析完整版第1页
Hadoop源代码分析完整版第2页
Hadoop源代码分析完整版第3页
Hadoop源代码分析完整版第4页
Hadoop源代码分析完整版第5页
Hadoop源代码分析完整版第6页
Hadoop源代码分析完整版第7页
Hadoop源代码分析完整版第8页
Hadoop源代码分析完整版第9页
Hadoop源代码分析完整版第10页
Hadoop源代码分析完整版第11页
Hadoop源代码分析完整版第12页
Hadoop源代码分析完整版第13页
Hadoop源代码分析完整版第14页
Hadoop源代码分析完整版第15页
Hadoop源代码分析完整版第16页
Hadoop源代码分析完整版第17页

试读结束, 可继续读3页

10积分/C币 立即下载