版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、Hadoop 源代碼分析(一)關(guān)鍵字: 分布式 云計(jì)算的競(jìng)爭(zhēng)技術(shù)是它的計(jì)算平臺(tái)。的大牛們用了下面 5 篇文章,介紹了它們的計(jì)算設(shè)施。Cluster: Chubby:GFS:BigTable: MapReduce:很快,Apache 上就出現(xiàn)了一個(gè)類似的解決方案,目前它們都屬于 Apache 的 Hadoop 項(xiàng)目,對(duì)應(yīng)的分別是: Chubby->ZooKeeperGFS->HDFSBigTable->HBase MapReduce->Hadoop目前,基于類似思想的 Open Source 項(xiàng)目還很多,如用于用戶分析的 Hive。HDFS 作為一個(gè)分布式文件系統(tǒng),是所有
2、這些項(xiàng)目的基礎(chǔ)。分析好 HDFS,有利于了解其他系統(tǒng)。由于 Hadoop 的 HDFS 和 MapReduce 是同一個(gè)項(xiàng)目,我們就把他們放在一塊,進(jìn)行分析。下圖是 MapReduce 整個(gè)項(xiàng)目的頂層包圖和他們的依賴關(guān)系。Hadoop 包之間的依賴關(guān)系比較復(fù)雜,是 HDFS 提供了一個(gè)分布式系統(tǒng)。這就造成了分文件系統(tǒng),該系統(tǒng)提供 API,可以本地文件系統(tǒng)和分布式文件系統(tǒng),甚至象 Amazon S3 這樣的布式文件系統(tǒng)的實(shí)現(xiàn),或者是分布式文件系統(tǒng)的底層的實(shí)現(xiàn),依賴于某些貌似的功能。功能的相互,造成了蜘蛛網(wǎng)型的依賴關(guān)系。一個(gè)典型的例子就是包 conf,conf 用于系統(tǒng)配置,它依賴于 fs,主要是
3、系統(tǒng),而部分的文件系統(tǒng)的功能,在包 fs 中被抽象了。配置文件的時(shí)候,需要使用文件Hadoop 的關(guān)鍵部分集中于圖中部分,這也是我們的重點(diǎn)。Hadoop 源代碼分析(二)下面給出了 Hadoop 的包的功能分析。Hadoop 源代碼分析(三)由于 Hadoop 的 MapReduce 和 HDFS 都有通信的需求,需要對(duì)通信的對(duì)象進(jìn)行序列化。Hadoop 并沒有采用 Java 的序列化,而是引入了它的系統(tǒng)。org.apache.hadoop.io 中定義了大量的可序列化對(duì)象,他們都實(shí)現(xiàn)了 Writable 接口。實(shí)現(xiàn)了 Writable 接口的一個(gè)典型例子如下:Java 代碼.5
4、..9.20.21.public class MyWritable implements Writable / Some data private int counter;private long timestamp;public void write(DataOutput out) throws IOException out.writeInt(counter); out.writeLong(timestamp);public void readFields(DataInput in) throws IOException
5、 counter = in.readInt(); timestamp = in.readLong();public static MyWritable read(DataInput in) throws IOException MyWritable w = new MyWritable(); w.readFields(in);return w;其中的 write 和 readFields 分別實(shí)現(xiàn)了把對(duì)象序列化和反序列化的功能,是 Writable 接口定義的兩個(gè)方法。下圖給出了龐大的 org.apache.hadoop.io 中對(duì)象的關(guān)系。PackageDependencestool提供一些
6、命令行工具,如 DistCp,archivemapreduceHadoop 的Map/Reduce 實(shí)現(xiàn)filecache提供 HDFS 文件的本地緩存,用于加快 Map/Reduce 的數(shù)據(jù)速度fs文件系統(tǒng)的抽象,可以理解為支持多種文件系統(tǒng)實(shí)現(xiàn)的統(tǒng)一文件接口hdfsHDFS,Hadoop 的分布式文件系統(tǒng)實(shí)現(xiàn)ipc一個(gè)簡(jiǎn)單的 IPC 的實(shí)現(xiàn),依賴于 io 提供的編功能參考:io表示層。將各種數(shù)據(jù)編碼/,方便于在網(wǎng)絡(luò)上傳輸net封裝部分網(wǎng)絡(luò)功能,如 DNS,socketsecurity用戶和用戶組信息conf系統(tǒng)的配置參數(shù)metrics系統(tǒng)統(tǒng)計(jì)數(shù)據(jù)的收集,屬于范疇util工具類record根據(jù)
7、DDL(數(shù)據(jù)描述語(yǔ)言)自動(dòng)生成他們的編函數(shù),目前可以提供 C+和 Javahttp基于 Jetty 的HTTP Servlet,用戶通過瀏覽器可以觀察文件系統(tǒng)的一些狀態(tài)信息和日志log提供 HTTP日志的 HTTP Servlet這里,我把 ObjectWritable 標(biāo)為紅色,是因?yàn)橄鄬?duì)于其他對(duì)象,它有不同的地位。當(dāng)我們討論 Hadoop 的 RPC 時(shí),我們會(huì)提到RPC 上交換的信息,必須是 Java 的基本類型,String 和Writable 接口的實(shí)現(xiàn)類,以及元素為以上類型的數(shù)組 。ObjectWritable 對(duì)象保存了一個(gè)可以在 RPC 上傳輸?shù)膶?duì)象和對(duì)象的類型信息。這樣,我們
8、就有了一個(gè)萬能的,可以用于客戶端/ 服務(wù)器間傳輸?shù)?Writable 對(duì)象。例如,我們要把上面例子中的對(duì)象作為 RPC 請(qǐng)求,需要根據(jù) MyWritable 創(chuàng)建一個(gè)ObjectWritable,ObjectWritable 往流里會(huì)寫如下信息對(duì)象類名長(zhǎng)度,對(duì)象類名,對(duì)象的串行化結(jié)果這樣,到了對(duì)端,ObjectWritable 可以根據(jù)對(duì)象類名創(chuàng)建對(duì)應(yīng)的對(duì)象,并解串行。應(yīng)該注意到,ObjectWritable 依賴于WritableFactories,那了Writable 子類對(duì)應(yīng)的工廠。我們需要把 MyWritable 的工廠,保存在 WritableFactories 中(通過Writab
9、leFactories.setFactory)。Hadoop 源代碼分析(五)介紹完 org.apache.hadoop.io 以后,我們開始來分析 org.apache.hadoop.rpc。RPC 采用客戶機(jī)/服務(wù)器模式。請(qǐng)求程序就是一個(gè)客戶機(jī),而服務(wù)提供程序就是一個(gè)服務(wù)器。當(dāng)我們討論 HDFS 的,通信可能發(fā)生在:····Client-NameNode 之間,其中 NameNode 是服務(wù)器Client-DataNode 之間,其中 DataNode 是服務(wù)器DataNode-NameNode 之間,其中 NameNode 是服務(wù)器DataNode
10、-DateNode 之間,其中某一個(gè) DateNode 是服務(wù)器,另一個(gè)是客戶端如果我們考慮 Hadoop 的 Map/Reduce 以后,這些系統(tǒng)間的通信就更復(fù)雜了。為了解決這些客戶機(jī)/服務(wù)器之間的通信,Hadoop 引入了一個(gè) RPC 框架。該 RPC 框架利用的 Java 的反射能力,避免了某些 RPC 解決方案中需要根據(jù)某種接口語(yǔ)言(如 CORBA 的IDL)生成存根和框架的問題。但是,該 RPC 框架要求調(diào)用的參數(shù)和返回結(jié)果必須是 Java 的基本類型,String 和Writable 接口的實(shí)現(xiàn)類,以及元素為以上類型的數(shù)組。同時(shí),接口方法應(yīng)該只拋出 IOException 異常。(
11、參考自om/blog/86306)既然是 RPC,當(dāng)然就有客戶端和服務(wù)器,當(dāng)然,org.apache.hadoop.rpc 也就有了類 Client 和類 Server。但是類 Server 是一個(gè)抽象類,類 RPC 封裝了 Server,利用反射,把某個(gè)對(duì)象的方法開放出來,變成 RPC 中的服務(wù)器。下圖是 org.apache.hadoop.rpc 的類圖。Hadoop 源代碼分析(六)既然是 RPC,自然就有客戶端和服務(wù)器,當(dāng)然,org.apache.hadoop.rpc 也就有了類 Client 和類 Server。在這里我們來仔細(xì)考察 org.apache.hadoop.rpc.Cli
12、ent。下面的圖包含了 org.apache.hadoop.rpc.Client 中的關(guān)鍵類和關(guān)鍵方法。由于 Client 可能和多個(gè) Server 通信,典型的一次 HDFS 讀,需要和 NameNode 打交道,也需要和某個(gè)/某些 DataNode 通信。這就意味著某一個(gè) Client 需要維護(hù)多個(gè)連接。同時(shí),為了減少不必要的連接,現(xiàn)在 Client 的做法是拿 ConnectionId(圖中最右側(cè))來做為 Connection 的 ID。ConnectionId 包括一個(gè) InetSocketAddress(IP 地址+端或主機(jī)名+端)對(duì)象和一個(gè)用戶信息對(duì)象。這就是說,同一個(gè)用戶到同一個(gè)
13、 InetSocketAddress 的通信將共享同接。連接被封裝在類 Client.Connection 中,所有的 RPC 調(diào)用,都是通過 Connection,進(jìn)行通信。一個(gè) RPC 調(diào)用,自然有輸入?yún)?shù), 輸出參數(shù)和可能的異常,同時(shí),為了區(qū)分在同一個(gè) Connection 上的不同調(diào)用,每個(gè)調(diào)用都有唯一的 id。調(diào)用是否結(jié)束也需要一個(gè)標(biāo)記,所有的這些都體現(xiàn)在對(duì)象 Client.Call 中。Connection 對(duì)象通過一個(gè) Hash 表,維護(hù)在這個(gè)連接上的所有 Call:Java 代碼1.private Hashtable<Integer, Call> calls = n
14、ew Hashtable<Integer, Call>();一個(gè) RPC 調(diào)用通過 addCall,把請(qǐng)求加到 Connection 里。為了能夠在這個(gè)框架上傳輸 Java 的基本類型,String 和Writable 接口的實(shí)現(xiàn)類,以及元素為以上類型的數(shù)組,我們一般把 Call 需要的參數(shù)打包成為 ObjectWritable 對(duì)象。Client.Connection 會(huì)通過 socket 連接服務(wù)器,連接后回校驗(yàn)客戶端/服務(wù)器的版本號(hào)(Client.ConnectionwriteHeader()方法),校驗(yàn)后就可以通過 Writable 對(duì)象來進(jìn)行請(qǐng)求的/應(yīng)答了。注意,每個(gè)Cl
15、ient.Connection 會(huì)起一個(gè)線程,不斷去獲取。socket,并將收到的結(jié)果解包,找出對(duì)應(yīng)的 Call,設(shè)置 Call 并通知結(jié)果已經(jīng)Call 使用 Obejct 的wait 和 notify,把 RPC 上的異步消息交互轉(zhuǎn)成同步調(diào)用。還有一點(diǎn)需要注意,一個(gè) Client 會(huì)有多個(gè) Client.Connection,這是一個(gè)很自然的結(jié)果。Hadoop 源代碼分析(七)聊完了 Client 聊 Server,按慣例,先把類圖貼出來。需要注意的是,這里的 Server 類是個(gè)抽象類,唯一抽象的地方,就是Java 代碼1.public abstract Writable call(Wri
16、table param, long receiveTime) throws IOException;這表明,Server 提供了一個(gè)架子,Server 的具體功能,需要具體類來完成。而具體類,當(dāng)然就是實(shí)現(xiàn) call 方法。我們先來分析 Server.Call,和 Client.Call 類似,Server.Call 包含了一次請(qǐng)求,其中,id 和 param 的含義和 Client.Call 是一致的。不同點(diǎn)在后面三個(gè)屬性,connection 是該 Call 來自的連接,當(dāng)然,當(dāng)請(qǐng)求處理結(jié)束時(shí),相應(yīng)的結(jié)果會(huì)通過相同的connection,給客戶端。屬性 timestamp 是請(qǐng)求到達(dá)的時(shí)間戳
17、,如果請(qǐng)求很長(zhǎng)時(shí)間沒被處理,對(duì)應(yīng)的連接會(huì)被關(guān)閉,客戶端也就知道出錯(cuò)了。最后的 response 是請(qǐng)求處理的結(jié)果,可能是一個(gè) Writable 的串行化結(jié)果,也可能一個(gè)異常的串行化結(jié)果。Server.Connection 維護(hù)了一個(gè)來之客戶端的 socket 連接。它處理版本校驗(yàn),請(qǐng)求并把請(qǐng)求到請(qǐng)求處理線程,接收處理結(jié)果并把結(jié)果給客戶端。Hadoop 的 Server 采用了 Java 的NIO,這樣的話就不需要為每一個(gè) socket 連接建立一個(gè)線程,socket 上的數(shù)據(jù)。在Server 中,只需要一個(gè)線程,就可以 accept 新的連接請(qǐng)求和socket 上的數(shù)據(jù),這個(gè)線程,就是上面圖里
18、的 Listener。請(qǐng)求處理線程一般有多個(gè),它們都是 Server.Handle 類的實(shí)例。它們的 run 方法循環(huán)地取出一個(gè) Server.Call,調(diào)用Server.call 方法,搜集結(jié)果并串行化,然后將結(jié)果放入 Responder 隊(duì)列中。對(duì)于處理完的請(qǐng)求,需要將結(jié)果寫回去,同樣,利用 NIO,只需要一個(gè)線程,相關(guān)的邏輯在 Responder 里。Hadoop 源代碼分析(八)(注:本節(jié)需要用到一些 Java 反射的背景)有了 Client 和 Server,很自然就能 RPC 啦。下面輪到 RPC.java 啦。一般來說,分布式對(duì)象一般都會(huì)要求根據(jù)接口生成存根和框架。如 CORBA
19、,可以通過 IDL,生成存根和框架。但是,在org.apache.hadoop.rpc,我們就不需要這樣的步驟了。上類圖。為了分析 Invoker,我們需要介紹一些 Java 反射實(shí)現(xiàn) Dynamic Proxy 的背景。Dynamic Proxy 是由兩個(gè) class 實(shí)現(xiàn)的:java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler,后者是一個(gè)接口。所謂 Dynamic Proxy 是這樣一種 class:它是在運(yùn)行時(shí)生成的 class,在生成它時(shí)你必須提供一組 interface 給它,然后該 class 就宣稱它實(shí)現(xiàn)了這些
20、 interface。這個(gè) Dynamic Proxy 其實(shí)就是一個(gè)典型的 Proxy 模式,它替你作實(shí)質(zhì)性的工作,在生成它的實(shí)例時(shí)你必須提供一個(gè)handler,由它接管實(shí)際的工作。這個(gè) handler,在 Hadoop 的 RPC 中,就是 Invoker 對(duì)象。我們可以簡(jiǎn)單地理解:就是你可以通過一個(gè)接口來生成一個(gè)類,這個(gè)類上的所有方法調(diào)用,都會(huì)傳遞到你生成類時(shí)傳遞的InvocationHandler 實(shí)現(xiàn)中。在 Hadoop 的 RPC 中,Invoker 實(shí)現(xiàn)了 InvocationHandler 的 invoke 方法(invoke 方法也是 InvocationHandler 的唯一
21、方法) 。Invoker 會(huì)把所有跟這次調(diào)用相關(guān)的調(diào)用方法名,參數(shù)類型列表,參數(shù)列表打包,然后利用前面我們分析過的 Client,通過socket 傳遞到服務(wù)器端。就是說,你在 proxy 類上的任何調(diào)用,都通過 Client到遠(yuǎn)方的服務(wù)器上。Invoker 使用 Invocation。Invocation 封裝了一個(gè)調(diào)用的所有相關(guān)信息,它的主要屬性有: methodName,調(diào)用方法名,parameterClasses,調(diào)用方法參數(shù)的類型列表和 parameters,調(diào)用方法參數(shù)。注意,它實(shí)現(xiàn)了 Writable 接口,可以串行化。RPC.Server 實(shí)現(xiàn)了 org.apache.hado
22、op.ipc.Server,你可以把一個(gè)對(duì)象,通過 RPC,升級(jí)成為一個(gè)服務(wù)器。服務(wù)器接收到的請(qǐng)求(通過 Invocation),解串行化以后,就變成了方法名,方法參數(shù)列表和參數(shù)列表。利用 Java 反射,我們就可以調(diào)用對(duì)應(yīng)的對(duì)象的方法。調(diào)用的結(jié)果再通過 socket,返回給客戶端,客戶端把結(jié)果解包后,就可以返回給 Dynamic Proxy 的使用者了。Hadoop 源代碼分析(九)一個(gè)典型的 HDFS 系統(tǒng)包括一個(gè) NameNode 和多個(gè) DataNode。NameNode 維護(hù)名字空間;而 DataNode數(shù)據(jù)塊。DataNode 負(fù)責(zé)數(shù)據(jù),一個(gè)數(shù)據(jù)塊在多個(gè) DataNode 中有備份
23、;而一個(gè) DataNode 對(duì)于一個(gè)塊最多只包含一個(gè)備份。所以我們可以簡(jiǎn)單地認(rèn)為 DataNode 上存了數(shù)據(jù)塊 ID 和數(shù)據(jù)塊內(nèi)容,以及他們的關(guān)系。一個(gè) HDFS 集群可能包含上千 DataNode 節(jié)點(diǎn),這些 DataNode 定時(shí)和 NameNode 通受NameNode 的指令。為了減輕 NameNode的負(fù)擔(dān),NameNode 上并不保存那個(gè) DataNode 上有那些數(shù)據(jù)塊的信息,而是通過 DataNode 啟動(dòng)時(shí)的上報(bào),來更新NameNode 上的表。DataNode 和NameNode 建立連接以后,就會(huì)不斷地和 NameNode 保持心跳。心跳的返回其還也包含了 NameNo
24、de 對(duì) DataNode 的一些命令,如刪除數(shù)據(jù)庫(kù)或者是把數(shù)據(jù)塊到另一個(gè) DataNode。應(yīng)該注意的是:NameNode發(fā)起到 DataNode 的請(qǐng)求,在這個(gè)通信過程中,它們是嚴(yán)格的客戶端/服務(wù)器架構(gòu)。DataNode 當(dāng)然也作為服務(wù)器接受來自客戶端的,處理數(shù)據(jù)塊讀/寫請(qǐng)求。DataNode 之間還會(huì)相互通信,執(zhí)行數(shù)據(jù)塊任務(wù),同時(shí),在客戶端做寫操作的時(shí)候,DataNode 需要相互配合,保證寫操作的一致性。下面我們就來具體分析一下 DataNode 的實(shí)現(xiàn)。DataNode 的實(shí)現(xiàn)包括兩部分,一部分是對(duì)本地?cái)?shù)據(jù)塊的管理,另一部分,就是和其他的實(shí)體打交道。我們先來看本地?cái)?shù)據(jù)塊管理部分。安裝
25、 Hadoop 的時(shí)候,我們會(huì)指定對(duì)應(yīng)的數(shù)據(jù)塊存放目錄,當(dāng)我們檢查數(shù)據(jù)塊存放目錄目錄時(shí),我們回發(fā)現(xiàn)下面有個(gè)叫 dfs 的目錄,所有的數(shù)據(jù)就存放在 dfs/data 里面。其中有兩個(gè)文件,storage 里存的東西是一些出錯(cuò)信息,貌似是版本不對(duì)云云。in_use.lock 是一個(gè)空文件,它的作用是如果需要對(duì)整個(gè)系統(tǒng)做排斥操作,應(yīng)用應(yīng)該獲取它上面的一個(gè)鎖。接下來是 3 個(gè)目錄,current 存的是當(dāng)前有效的數(shù)據(jù)塊,detach 存的是快照(snapshot,目前沒有實(shí)現(xiàn)),tmp 保存的是一些操作需要的臨時(shí)數(shù)據(jù)塊。但我們進(jìn)入 current 目錄以后,就會(huì)發(fā)現(xiàn)有一系列的數(shù)據(jù)塊文件和數(shù)據(jù)塊元數(shù)據(jù)文
26、件。同時(shí)還有一些子目錄,它們的名字是subdir0 到 subdir63,子目錄下也有數(shù)據(jù)塊文件和數(shù)據(jù)塊元數(shù)據(jù)。這是因?yàn)?HDFS 限定了每個(gè)目錄存放數(shù)據(jù)塊文件的數(shù)量,多了以后會(huì)創(chuàng)建子目錄來保存。數(shù)據(jù)塊文件顯然保存了 HDFS 中的數(shù)據(jù),數(shù)據(jù)塊最大可以到 64M。每個(gè)數(shù)據(jù)塊文件都會(huì)有對(duì)應(yīng)的數(shù)據(jù)塊元數(shù)據(jù)文件。里面存放的是數(shù)據(jù)塊的校驗(yàn)信息。下面是數(shù)據(jù)塊文件名和它的元數(shù)據(jù)文件名的例子:blk_ blk_33_242812.meta上面的例子中,3 是數(shù)據(jù)塊的 ID 號(hào),242812 是數(shù)據(jù)塊的版本號(hào),用于一致性檢查。在 current 目錄下還有下面幾個(gè)文件:VERSION,保存了一些文件系統(tǒng)的元信
27、息。dncp_block_verification.log.curr 和 dncp_block_verification.log.prev,它性檢查需要的信息。了一些 DataNode 對(duì)文件系定時(shí)統(tǒng)做一致Hadoop 源代碼分析(一零)在繼續(xù)分析 DataNode 之前,我們有必要看一下系統(tǒng)的工作狀態(tài)。啟動(dòng) HDFS 的時(shí)候,我們可以選擇以下啟動(dòng)參數(shù):·····FORMAT("-format"):格式化系統(tǒng)REGULAR("-regular"):正常啟動(dòng)UPGRADE("-upgrade&qu
28、ot;):升級(jí)ROLLBACK("-rollback"):回滾FINALIZE("-finalize"):提交·IMPORT("-importCheckpoint"):從 Checkpoint 恢復(fù)。作為一個(gè)大型的分布式系統(tǒng),Hadoop 內(nèi)部實(shí)現(xiàn)了一套升級(jí)機(jī)制(g/hadoop/Hadoop Upgrade )。upgrade 參數(shù)就是為了這個(gè)目的而存在的,當(dāng)然,升級(jí)可能,也可能失敗。如果失敗了,那就用 rollback 進(jìn)行回滾;如果過了一段時(shí)間,系統(tǒng)運(yùn)行正常,那就可以通過 finalize,正式提交這次升級(jí)(跟數(shù)據(jù)庫(kù)有
29、點(diǎn)像啊)。importCheckpoint 選項(xiàng)用于 NameNode 發(fā)生故障后,從某個(gè)檢查點(diǎn)恢復(fù)。有了上面的描述,我們得到下面左邊的狀態(tài)圖:大家應(yīng)該注意到,上面的升級(jí)/回滾/提交都不可能一下就搞定,就是說,系統(tǒng)故障時(shí),它可能處于上面右邊狀態(tài)中的某一個(gè)。特別是分布式的各個(gè)節(jié)點(diǎn)上,甚至可能出現(xiàn)某些節(jié)點(diǎn)已經(jīng)升級(jí)類似于數(shù)據(jù)庫(kù)事務(wù)的升級(jí)機(jī)制也就不是很奇怪。,但有些節(jié)點(diǎn)可能處于中間狀態(tài)的情況,所以 Hadoop 采用大家先理解一下上面的狀態(tài)圖,它是下面我們要介紹 DataNode的基礎(chǔ)。Hadoop 源代碼分析(一一)我們來看一下升級(jí)/回滾/提交時(shí)的 DataNode 上會(huì)發(fā)生什么(在類 DataSt
30、orage 中實(shí)現(xiàn))。前面我們提到過 VERSION 文件,它保存了一些文件系統(tǒng)的元信息,這個(gè)文件在系統(tǒng)升級(jí)時(shí),會(huì)發(fā)生對(duì)應(yīng)的變化。升級(jí)時(shí),NameNode 會(huì)將新的版本號(hào),通過 DataNode 的登錄應(yīng)答返回。DataNode 收到以后,會(huì)將當(dāng)前的數(shù)據(jù)塊文件目錄改名, 從 current 改名為 previous.tmp,建立一個(gè) snapshot,然后重建 current 目錄。重建包括重建 VERSION 文件,重建對(duì)應(yīng)的子目錄,然后建立數(shù)據(jù)塊文件和數(shù)據(jù)塊元數(shù)據(jù)文件到 previous.tmp 的硬連接。建立硬連接意味著在系統(tǒng)中只保留一份數(shù)據(jù)塊文件和數(shù)據(jù)塊元數(shù)據(jù)文件,current 和
31、previous.tmp 中的相應(yīng)文件,在中,只保留一份。當(dāng)所有的這些工作完成以后,會(huì)在current 里寫入新的 VERSION 文件,并將 previous.tmp 目錄改名為 previous,完成升級(jí)。了解了升級(jí)的過程以后,回滾就相對(duì)簡(jiǎn)單。因?yàn)檎f有的舊版本信息都保存在 previous 目錄里?;貪L首先將 current 目錄改名為removed.tmp,然后將 previous 目錄改名為 current,最后刪除 removed.tmp 目錄。提交的過程,就是將上面的 previous 目錄改名為 finalized.tmp,然后啟動(dòng)一個(gè)線程,將該目錄刪除。下圖給出了上面的過程:需
32、要注意的是,HDFS 的升級(jí),往往只是支持從某一個(gè)特點(diǎn)的老版本升級(jí)到當(dāng)前版本?;貪L時(shí)能夠恢復(fù)到的版本,也是 previous中的版本。下面我們繼續(xù)分析 DataNode。文字分析完 DataNode在文件上的數(shù)據(jù)以后,我們來看一下運(yùn)行時(shí)對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)。從大到小,Hadoop 中最大的結(jié)構(gòu)是Storage,最小的結(jié)構(gòu),在 DataNode 上是 block。類 Storage 保存了和圖如下:相關(guān)的信息,它繼承了 StorageInfo,應(yīng)用于 DataNode 的 DataStorage,則繼承了 Storage,總體類StorageInfo 包含了 3 個(gè)字段,分別是 layoutVersi
33、on:版本號(hào),如果 Hadoop 調(diào)整文件結(jié)構(gòu)布局,版本號(hào)就會(huì)修改,這樣可以保證文件結(jié)構(gòu)和應(yīng)用一致。namespaceID 是 Storage 的 ID,cTime,creation time。和 StorageInfo 相比,Storage 就是個(gè)大家伙了。Storage 可以包含多個(gè)根(參考配置項(xiàng) dfs.data.dir 的說明),這些根通過 Storage 的內(nèi)部類 StorageDirectory 來表示 。StorageDirectory 中最重要的方法是 analyzeStorage,它將根據(jù)系統(tǒng)啟動(dòng)時(shí)的參數(shù)和我們上面提到的一些統(tǒng)現(xiàn)在的狀態(tài)。StorageDirectory 可能
34、處于以下的某一個(gè)狀態(tài)(與系統(tǒng)的工作狀態(tài)一定的對(duì)應(yīng)):條件,返回系NON_EXISTENT:指定的目錄不存在;NOT_FORMATTED:指定的目錄存在但未被格式化;COMPLETE_UPGRADE:previous.tmp 存在,current 也存在RECOVER_UPGRADE:previous.tmp 存在,current 不存在COMPLETE_FINALIZE:finalized.tmp 存在,current 也存在COMPLETE_ROLLBACK:removed.tmp 存在,current 也存在,previous 不存在RECOVER_ROLLBACK:removed.tmp
35、 存在,current 不存在,previous 存在COMPLETE_CHECKPOINT:lastcheckpoint.tmp 存在,current 也存在RECOVER_CHECKPOINT:lastcheckpoint.tmp 存在,current 不存在NORMAL:普通工作模式。StorageDirectory 處于某些狀態(tài)是通過發(fā)生對(duì)應(yīng)狀態(tài)改變需要的工作文件夾和正常工作的 current 夾來進(jìn)行要的工作文件夾包括:。狀態(tài)改變需previous:用于升級(jí)后保存以前版本的文件 previous.tmp:用于升級(jí)過程中保存以前版本的文件removed.tmp:用于回滾過程中保存文件
36、finalized.tmp:用于提交過程中保存文件 lastcheckpoint.tmp:應(yīng)用于從 NameNode 中,導(dǎo)入一個(gè)檢查點(diǎn)previous.checkpoint:應(yīng)用于從 NameNode 中,結(jié)束導(dǎo)入一個(gè)檢查點(diǎn)有了這些狀態(tài),就可以對(duì)系統(tǒng)進(jìn)行恢復(fù)(通過方法 doRecover)?;謴?fù)的動(dòng)作如下(結(jié)合上面的狀態(tài)轉(zhuǎn)移圖):COMPLETE_UPGRADE:mv previous.tmp -> previous RECOVER_UPGRADE:mv previous.tmp -> currentCOMPLETE_FINALIZE:rm finalized.tmpCOMPLE
37、TE_ROLLBACK:rm removed.tmp RECOVER_ROLLBACK:mv removed.tmp -> currentCOMPLETE_CHECKPOINT:mv lastcheckpoint.tmp -> previous.checkpoint RECOVER_CHECKPOINT:mv lastcheckpoint.tmp -> current我們以 RECOVER_UPGRADE 為例,分析一下。根據(jù)升級(jí)的過程,1. current->previous.tmp2. 重建current3. previous.tmp->previous當(dāng)我們
38、發(fā)現(xiàn) previous.tmp 存在,current 不存在,我們知道只需要將 previous.tmp 改為 current,就能恢復(fù)到未升級(jí)時(shí)的狀態(tài)。StorageDirectory 還管理著文件系統(tǒng)的元信息,就是我們上面提過 StorageInfo 信息,當(dāng)然,StorageDirectory 還保存每個(gè)具體用途的信息。這些信息,其實(shí)都在VERSION 文件中,StorageDirectory 中的 read/write 方法,就是用于對(duì)這個(gè)文件進(jìn)行讀/寫。下面是某一個(gè) DataNode 的VERSION 文件的例子:配置文件代碼.6.#Fri Nov 14 10:27:3
39、5 CST 2008namespaceID=storageID=DS-697414267--50010-1226629655026cTime=0 storageType=DATA_NODE layoutVersion=-16對(duì) StorageDirectory 的排他操作需要鎖,還記得我們?cè)诜治鱿到y(tǒng)目錄時(shí)提到的 in_use.lock 文件嗎?它就是用來給整個(gè)系統(tǒng)加/用的。StorageDirectory 提供了對(duì)應(yīng)的 lock 和 unlock 方法。分析完 StorageDirectory 以后,Storage 類就很簡(jiǎn)單了。基本上都是對(duì)一系列 StorageDirect
40、ory 的操作,同時(shí) Storage 提供一些輔助方法。DataStorage 是 Storage 的子類,專門應(yīng)用于 DataNode。上面我們對(duì) DataNode 的升級(jí)/回滾/提交過程,就是對(duì) DataStorage 的doUpgrade/doRollback/doFinalize 分析得到的。DataStorage 提供了 format 方法,用于創(chuàng)建 DataNode 上的 Storage,同時(shí),利用 StorageDirectory,DataStorage 管理統(tǒng)的狀態(tài)。系Hadoop 源代碼分析(一二)分析完 Storage 相關(guān)的類以后,我們來看下一個(gè)大家伙,F(xiàn)SDataset
41、 相關(guān)的類。上面介紹 Storage 時(shí),我們并沒有涉及到數(shù)據(jù)塊 Block 的操作,所有和數(shù)據(jù)塊相關(guān)的操作,都在 FSDataset 相關(guān)的類中進(jìn)行處理。下面是類圖:Block 是對(duì)一個(gè)數(shù)據(jù)塊的抽象,通過前面的討論我們知道一個(gè) Block 對(duì)應(yīng)著兩個(gè)文件,其中一個(gè)存數(shù)據(jù),一個(gè)存校驗(yàn)信息,如下:blk_ blk_33_242812.meta上面的信息中,blockId 是3,242812 是數(shù)據(jù)塊的版本號(hào),當(dāng)然,系統(tǒng)還會(huì)保存數(shù)據(jù)塊的大小,在類中是屬性 numBytes。Block 提供了一系列的方法來操作對(duì)象的屬性。DatanodeBlockInfo 存放的是 Block 在文件系統(tǒng)上的信息。
42、它保存了 Block 存放的卷(FSVolume),文件名和 detach 狀態(tài)。這里有必要解釋一下 detach 狀態(tài):我們前面分析過,系統(tǒng)在升級(jí)時(shí)會(huì)創(chuàng)建一個(gè) snapshot,snapshot 的文件和 current 里的數(shù)據(jù)塊文件和數(shù)據(jù)塊元文件是通過硬,指向了相同的內(nèi)容。當(dāng)我們需要改變 current 里的文件時(shí),如果不進(jìn)行 detach 操作,那么修改的內(nèi)容就會(huì)影響 snapshot 里的文件,這時(shí),我們需要將對(duì)應(yīng)的硬解除掉。方法很簡(jiǎn)單,就是在臨時(shí)文件夾里,文件,然后將臨時(shí)文件改名成為 current 里的對(duì)應(yīng)文件,這樣的話,current 里的文件和 snapshot 里的文件就
43、detach 了。這樣的技術(shù),也叫 copy-on-write,是一種有效提高系統(tǒng)性能的方法。DatanodeBlockInfo 中的 detachBlock,能夠?qū)?Block 對(duì)應(yīng)的數(shù)據(jù)文件和元數(shù)據(jù)文件進(jìn)行 detach 操作。介紹完類 Block 和 DatanodeBlockInfo 后,我們來看 FSVolumeSet,F(xiàn)SVolume 和 FSDir。我們知道在一個(gè) DataNode 上可以指定多個(gè) Storage 來數(shù)據(jù)塊,由于 HDFS 規(guī)定了一個(gè)目錄能存放 Block 的數(shù)目,所以一個(gè) Storage 上存在多個(gè)目錄。對(duì)應(yīng)的,F(xiàn)SDataset 中用 FSVolume 來對(duì)應(yīng)
44、一個(gè) Storage,F(xiàn)SDir 對(duì)應(yīng)一個(gè)目錄,所有的 FSVolume 由 FSVolumeSet 管理,F(xiàn)SDataset 中通過一個(gè) FSVolumeSet 對(duì)象,就可以管理它的所有空間。FSDir 對(duì)應(yīng)著 HDFS 中的一個(gè)目錄,目錄里存放著數(shù)據(jù)塊文件和它的元文件。FSDir 的一個(gè)重要的操作,就是在添加一個(gè) Block 時(shí),根據(jù)需要有時(shí)會(huì)擴(kuò)展目錄結(jié)構(gòu),上面提過,一個(gè) Storage 上存在多個(gè)目錄,所有的目錄,都對(duì)應(yīng)著一個(gè) FSDir,目錄的關(guān)系,也由 FSDir 保存。FSDir 的 getBlockInfo 方法分析目錄下的所有數(shù)據(jù)塊文件信息,生成 Block 對(duì)象,存放到一個(gè)集
45、合中 。getVolumeMap 方法能,則會(huì)建立 Block 和 DatanodeBlockInfo 的關(guān)系。以上兩個(gè)方法,用于系統(tǒng)啟動(dòng)時(shí)搜集所有的數(shù)據(jù)塊信息,便于后面快速。FSVolume 對(duì)應(yīng)著是某一個(gè) Storage。數(shù)據(jù)塊文件,detach 文件和臨時(shí)文件都是通過 FSVolume 來管理的,這個(gè)其實(shí)很自然,在同一個(gè)系統(tǒng)上移動(dòng)文件,往往只需要修改文件信息,不需要搬數(shù)據(jù)。FSVolume 有一個(gè) recoverDetachedBlocks 的方法,用于恢復(fù) detach 文件。和 Storage 的狀態(tài)管理一樣,detach 文件有可能在文件時(shí)系統(tǒng),需要對(duì) detach 的操作進(jìn)行回復(fù)
46、。FSVolume 還會(huì)啟動(dòng)一個(gè)線程,不斷更新 FSVolume 所在文件系統(tǒng)的剩余容量。創(chuàng)建 Block 的時(shí)候,系統(tǒng)會(huì)根據(jù)各個(gè) FSVolume 的容量,來確認(rèn) Block 的存放位置。FSVolumeSet 就不討論了,它管理著所有的 FSVolume。HDFS 中,對(duì)一個(gè) chunk 的寫會(huì)使文件處于活躍狀態(tài),F(xiàn)SDataset 中引入了類 ActiveFile。ActiveFile 對(duì)象保存了一個(gè)文件,和操作這個(gè)文件的線程。注意,線程有可能有多個(gè)。ActiveFile 的構(gòu)造函數(shù)會(huì)自動(dòng)地把當(dāng)前線程加入其中。有了上面的基礎(chǔ),我們可以開始分析 FSDataset。FSDataset 實(shí)現(xiàn)
47、了接口 FSDatasetInterface。FSDatasetInterface 是 DataNode對(duì)底層的抽象。下面給出了 FSDataset 的關(guān)鍵成員變量:FSVolumeSet volumes;private HashMap<Block,ActiveFile> ongoingCreates = new HashMap<Block,ActiveFile>();private HashMap<Block,DatanodeBlockInfo> volumeMap = null;其中,volumes 就是 FSDataset 使用的所有 Storage,
48、ongoingCreates 是 Block 到 ActiveFile 的,也就是說,說有正在創(chuàng)建的 Block,都會(huì)在 ongoingCreates 里。下面我們討論 FSDataset 中的方法。public long getMetaDataLength(Block b) throws IOException;得到一個(gè) block 的元數(shù)據(jù)長(zhǎng)度。通過 block 的ID,找對(duì)應(yīng)的元數(shù)據(jù)文件,返回文件長(zhǎng)度。public MetaDataInputStream getMetaDataInputStream(Block b) throws IOException;得到一個(gè) block 的元數(shù)據(jù)輸
49、入流。通過 block 的ID,找對(duì)應(yīng)的元數(shù)據(jù)文件,在上面打開輸入流。下面對(duì)于類似的簡(jiǎn)單方法,我們就不再仔細(xì)討論了。public boolean metaFileExists(Block b) throws IOException;block 的元數(shù)據(jù)的元數(shù)據(jù)文件是否存在。簡(jiǎn)單方法。public long getLength(Block b) throws IOException;block 的長(zhǎng)度。簡(jiǎn)單方法。public Block getStoredBlock(long blkid) throws IOException;通過 Block 的ID,找到對(duì)應(yīng)的 Block。簡(jiǎn)單方法。publ
50、ic InputStream getBlockInputStream(Block b) throws IOException;public InputStream getBlockInputStream(Block b, long seekOffset) throws IOException;得到 Block 數(shù)據(jù)的輸入流。簡(jiǎn)單方法。public BlockInputStreams getTmpInputStreams(Block b, long blkoff, long ckoff) throws IOException;得到 Block 的臨時(shí)輸入流。注意,臨時(shí)輸入流是指對(duì)應(yīng)的文件處于 t
51、mp 目錄中。新創(chuàng)建塊時(shí),塊數(shù)據(jù)應(yīng)該寫在 tmp 目錄中,直到寫操作,文件才會(huì)被移動(dòng)到 current 目錄中,如果失敗,就影響 current 目錄了。簡(jiǎn)單方法。public BlockWriteStreams writeToBlock(Block b, boolean isRecovery) throws IOException;得到一個(gè) block 的輸出流。BlockWriteStreams 既包含了數(shù)據(jù)輸出流,也包含了元數(shù)據(jù)(校驗(yàn)文件)輸出流,這是一個(gè)相當(dāng)復(fù)雜的方法。參數(shù) isRecovery 說明這次寫是不是對(duì)以前失敗的寫的一次恢復(fù)操作。我們先看正常的寫操作流程:首先,如果輸入的
52、block 是個(gè)正常的數(shù)據(jù)塊,或當(dāng)前的 block 已經(jīng)有線程在寫,writeToBlock 會(huì)拋出一個(gè)異常。否則,將創(chuàng)建相應(yīng)的臨時(shí)數(shù)據(jù)文件和臨時(shí)元數(shù)據(jù)文件,并把相關(guān)信息,創(chuàng)建一個(gè) ActiveFile 對(duì)象,到 ongoingCreates 中,并創(chuàng)建返回的 BlockWriteStreams。前面我們已經(jīng)提過,建立新的 ActiveFile 時(shí),當(dāng)前線程會(huì)自動(dòng)保存在 ActiveFile 的 threads 中。我們以 blk_ 件 tmp/blk_是版本號(hào)。3 為例,當(dāng) DataNode 需要為 Block ID 為3 做為臨時(shí)數(shù)據(jù)文件,對(duì)應(yīng)的 meta 文件是 tmp/blk_3 創(chuàng)建
53、寫流時(shí),DataNode 創(chuàng)建文3_.meta。其中isRecovery 為 true 時(shí),表明我們需要從某一次不的寫中恢復(fù),流程相對(duì)于正常流程復(fù)雜。如果不的寫是由于提交(參考 finalizeBlock 方法)后的確認(rèn)信息沒有收到,先創(chuàng)建一個(gè) detached 文件(備份)。接著,writeToBlock 檢查是否有還有對(duì)文件寫的線程,如果有,則通過線程的 interrupt 方法,強(qiáng)制結(jié)束線程。這就是說,如果有線程還在寫對(duì)應(yīng)的文件塊,該線程將被終止。同時(shí),從 ongoingCreates 中移除對(duì)應(yīng)的信息。接下來將根據(jù)臨時(shí)文件是否存在,創(chuàng)建/復(fù)用臨時(shí)數(shù)據(jù)文件和臨時(shí)數(shù)據(jù)元文件。后續(xù)操作就和正
54、常流程一樣,根據(jù)相關(guān)信息,創(chuàng)建一個(gè) ActiveFile 對(duì)象,到 ongoingCreates 中由于這塊涉及了一些 HDFS 寫文件時(shí)的策略,以后我們還會(huì)繼續(xù)討論這個(gè)話題。public void updateBlock(Block oldblock, Block newblock) throws IOException;更新一個(gè) block。這也是一個(gè)相當(dāng)復(fù)雜的方法。updateBlock 的最外層是一個(gè)死循環(huán),循環(huán)的結(jié)束條件,是沒有任何和這個(gè)數(shù)據(jù)塊相關(guān)的寫線程。每次循環(huán),updateBlock 都會(huì)去調(diào)用一個(gè)叫 tryUpdateBlock 的內(nèi)部方法。tryUpdateBlock 發(fā)現(xiàn)
55、已經(jīng)沒有線程在寫這個(gè)塊,就會(huì)跟新和這個(gè)數(shù)據(jù)塊相關(guān)的信息包括元文件和內(nèi)存中的表 volumeMap。如果 tryUpdateBlock 發(fā)現(xiàn)還有活躍的線程和該塊關(guān)聯(lián),那么,updateBlock 會(huì)試圖結(jié)束該線程,并等在 join 上等待。public void finalizeBlock(Block b) throws IOException;提交(或叫:結(jié)束 finalize)通過 writeToBlock 打開的 block,這意味著寫過程沒有出錯(cuò),可以正式把 Block 從tmp 文件夾放到 current 文件夾。在 FSDataset 中,finalizeBlock 將從 ongoi
56、ngCreates 中刪除對(duì)應(yīng)的 block,同時(shí)將 block 對(duì)應(yīng)的 DatanodeBlockInfo,放入volumeMap 中。我們還是以 blk_ DataNode 將把 tmp/blk_3 為例,當(dāng) DataNode 提交 Block ID 為3 移到 current 下某一個(gè)目錄,以 subdir12 為例,這是3 數(shù)據(jù)塊文件時(shí)tmp/blk_ current/subdir12 下。3 將會(huì)挪到 current/subdir12/blk_3。對(duì)應(yīng)的 meta 文件也在目錄public void unfinalizeBlock(Block b) throws IOExceptio
57、n;取消通過 writeToBlock 打開的 block,與 finalizeBlock 方法作用相反。簡(jiǎn)單方法。public boolean isValidBlock(Block b);該 Block 是否有效。簡(jiǎn)單方法。public void invalidate(Block invalidBlks) throws IOException;使block 變?yōu)闊o效。簡(jiǎn)單方法。public void validateBlockMetadata(Block b) throws IOException;檢查 block 的有效性。簡(jiǎn)單方法。Hadoop 源代碼分析(一三)通過上面的一系列介紹,我們知道了 DataNode 工作時(shí)的文件結(jié)構(gòu)和文件結(jié)構(gòu)在內(nèi)存中的對(duì)應(yīng)對(duì)象。下面我們可以來開始分析DataNode 上的動(dòng)態(tài)行為。首先我們來分析 DataXceiverServer 和 Da
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 《基因突變和基因重組》教學(xué)設(shè)計(jì)1
- 課題申報(bào)參考:競(jìng)合供應(yīng)鏈企業(yè)社會(huì)責(zé)任審計(jì)、運(yùn)營(yíng)與融資策略研究
- 課題申報(bào)參考:檢察公益訴訟立法研究
- 2025年上半年水產(chǎn)漁業(yè)生產(chǎn)情況總結(jié)及下半年工作安排(三篇)
- 二零二五版房地產(chǎn)土地使用權(quán)交易爭(zhēng)議解決協(xié)議3篇
- 影視劇臨時(shí)演員聘用協(xié)議2025版2篇
- 2025年度個(gè)人與派遣公司教育培訓(xùn)派遣合同范本4篇
- 二零二五年鍋爐維修安全風(fēng)險(xiǎn)評(píng)估與處理協(xié)議3篇
- 二零二五版新材料產(chǎn)業(yè)臨時(shí)用工聘用管理協(xié)議3篇
- 2025年香港公司股權(quán)轉(zhuǎn)讓手續(xù)糾紛解決合同3篇
- 慈溪高一期末數(shù)學(xué)試卷
- 天津市武清區(qū)2024-2025學(xué)年八年級(jí)(上)期末物理試卷(含解析)
- 《徐霞客傳正版》課件
- 江西硅博化工有限公司年產(chǎn)5000噸硅樹脂項(xiàng)目環(huán)境影響評(píng)價(jià)
- 高端民用航空復(fù)材智能制造交付中心項(xiàng)目環(huán)評(píng)資料環(huán)境影響
- 量子醫(yī)學(xué)成像學(xué)行業(yè)研究報(bào)告
- DB22T 3268-2021 糧食收儲(chǔ)企業(yè)安全生產(chǎn)標(biāo)準(zhǔn)化評(píng)定規(guī)范
- 辦事居間協(xié)議合同范例
- 正念減壓療法詳解課件
- 學(xué)校校本課程《英文電影鑒賞》文本
- 華為HCSA-Presales-IT售前認(rèn)證備考試題及答案
評(píng)論
0/150
提交評(píng)論