#
screen 進(jìn)入screen模式
C-a c 在當(dāng)前screen下建立新的窗口
exit 退出當(dāng)前窗口,如果它是此screen的唯一窗口時(shí),此screen也將完全退出。
C-a d 暫時(shí)斷開(kāi)screen會(huì)話(huà)
screen -ls 查看有哪些screen。
screen -r id 打開(kāi)編號(hào)為id的screen窗口。
C-a w 顯示所有窗口列表
C-a n 切換到下一個(gè)窗口
C-a p 切換到前一個(gè)窗口(與C-a n相對(duì))
最無(wú)敵命令 screen --help
原文:http://hadoop.apache.org/core/docs/current/hdfs_design.html
一、前提和設(shè)計(jì)目標(biāo)1
、硬件錯(cuò)誤是常態(tài),而非異常情況,HDFS
可能是有成百上千的server
組成,任何一個(gè)組件都有可能一直失效,因此錯(cuò)誤檢測(cè)和快速、自動(dòng)的恢復(fù)是HDFS
的核心架構(gòu)目標(biāo)。2
、跑在HDFS
上的應(yīng)用與一般的應(yīng)用不同,它們主要是以流式讀為主,做批量處理;比之關(guān)注數(shù)據(jù)訪(fǎng)問(wèn)的低延遲問(wèn)題,更關(guān)鍵的在于數(shù)據(jù)訪(fǎng)問(wèn)的高吞吐量。3
、HDFS
以支持大數(shù)據(jù)集合為目標(biāo),一個(gè)存儲(chǔ)在上面的典型文件大小一般都在千兆至T
字節(jié),一個(gè)單一HDFS
實(shí)例應(yīng)該能支撐數(shù)以千萬(wàn)計(jì)的文件。4
、 HDFS
應(yīng)用對(duì)文件要求的是write-one-read-many
訪(fǎng)問(wèn)模型。一個(gè)文件經(jīng)過(guò)創(chuàng)建、寫(xiě),關(guān)閉之后就不需要改變。這一假設(shè)簡(jiǎn)化了數(shù)據(jù)一致性問(wèn)題,使高吞吐量的數(shù)據(jù)訪(fǎng)問(wèn)成為可能。典型的如MapReduce
框架,或者一個(gè)web crawler
應(yīng)用都很適合這個(gè)模型。5
、移動(dòng)計(jì)算的代價(jià)比之移動(dòng)數(shù)據(jù)的代價(jià)低。一個(gè)應(yīng)用請(qǐng)求的計(jì)算,離它操作的數(shù)據(jù)越近就越高效,這在數(shù)據(jù)達(dá)到海量級(jí)別的時(shí)候更是如此。將計(jì)算移動(dòng)到數(shù)據(jù)附近,比之將數(shù)據(jù)移動(dòng)到應(yīng)用所在顯然更好,HDFS
提供給應(yīng)用這樣的接口。6
、在異構(gòu)的軟硬件平臺(tái)間的可移植性。二、Namenode
和Datanode
HDFS
采用master/slave
架構(gòu)。一個(gè)HDFS
集群是有一個(gè)Namenode
和一定數(shù)目的Datanode
組成。Namenode
是一個(gè)中心服務(wù)器,負(fù)責(zé)管理文件系統(tǒng)的namespace
和客戶(hù)端對(duì)文件的訪(fǎng)問(wèn)。Datanode
在集群中一般是一個(gè)節(jié)點(diǎn)一個(gè),負(fù)責(zé)管理節(jié)點(diǎn)上它們附帶的存儲(chǔ)。在內(nèi)部,一個(gè)文件其實(shí)分成一個(gè)或多個(gè)block
,這些block
存儲(chǔ)在Datanode
集合里。Namenode
執(zhí)行文件系統(tǒng)的namespace
操作,例如打開(kāi)、關(guān)閉、重命名文件和目錄,同時(shí)決定block
到具體Datanode
節(jié)點(diǎn)的映射。Datanode
在Namenode
的指揮下進(jìn)行block
的創(chuàng)建、刪除和復(fù)制。Namenode
和Datanode
都是設(shè)計(jì)成可以跑在普通的廉價(jià)的運(yùn)行linux
的機(jī)器上。HDFS
采用java
語(yǔ)言開(kāi)發(fā),因此可以部署在很大范圍的機(jī)器上。一個(gè)典型的部署場(chǎng)景是一臺(tái)機(jī)器跑一個(gè)單獨(dú)的Namenode
節(jié)點(diǎn),集群中的其他機(jī)器各跑一個(gè)Datanode
實(shí)例。這個(gè)架構(gòu)并不排除一臺(tái)機(jī)器上跑多個(gè)Datanode
,不過(guò)這比較少見(jiàn)。
單一節(jié)點(diǎn)的Namenode
大大簡(jiǎn)化了系統(tǒng)的架構(gòu)。Namenode
負(fù)責(zé)保管和管理所有的HDFS
元數(shù)據(jù),因而用戶(hù)數(shù)據(jù)就不需要通過(guò)Namenode
(也就是說(shuō)文件數(shù)據(jù)的讀寫(xiě)是直接在Datanode
上)。三、文件系統(tǒng)的namespace
HDFS
支持傳統(tǒng)的層次型文件組織,與大多數(shù)其他文件系統(tǒng)類(lèi)似,用戶(hù)可以創(chuàng)建目錄,并在其間創(chuàng)建、刪除、移動(dòng)和重命名文件。HDFS
不支持user quotas
和訪(fǎng)問(wèn)權(quán)限,也不支持鏈接(link)
,不過(guò)當(dāng)前的架構(gòu)并不排除實(shí)現(xiàn)這些特性。Namenode
維護(hù)文件系統(tǒng)的namespace
,任何對(duì)文件系統(tǒng)namespace
和文件屬性的修改都將被Namenode
記錄下來(lái)。應(yīng)用可以設(shè)置HDFS
保存的文件的副本數(shù)目,文件副本的數(shù)目稱(chēng)為文件的 replication
因子,這個(gè)信息也是由Namenode
保存。四、數(shù)據(jù)復(fù)制 HDFS
被設(shè)計(jì)成在一個(gè)大集群中可以跨機(jī)器地可靠地存儲(chǔ)海量的文件。它將每個(gè)文件存儲(chǔ)成block
序列,除了最后一個(gè)block
,所有的block
都是同樣的大小。文件的所有block
為了容錯(cuò)都會(huì)被復(fù)制。每個(gè)文件的block
大小和replication
因子都是可配置的。Replication
因子可以在文件創(chuàng)建的時(shí)候配置,以后也可以改變。HDFS
中的文件是write-one
,并且嚴(yán)格要求在任何時(shí)候只有一個(gè)writer
。Namenode
全權(quán)管理block
的復(fù)制,它周期性地從集群中的每個(gè)Datanode
接收心跳包和一個(gè)Blockreport
。心跳包的接收表示該Datanode
節(jié)點(diǎn)正常工作,而Blockreport
包括了該Datanode
上所有的block
組成的列表。

1、副本的存放,副本的存放是HDFS可靠性和性能的關(guān)鍵。HDFS采用一種稱(chēng)為rack-aware的策略來(lái)改進(jìn)數(shù)據(jù)的可靠性、有效性和網(wǎng)絡(luò)帶寬的利用。這個(gè)策略實(shí)現(xiàn)的短期目標(biāo)是驗(yàn)證在生產(chǎn)環(huán)境下的表現(xiàn),觀(guān)察它的行為,構(gòu)建測(cè)試和研究的基礎(chǔ),以便實(shí)現(xiàn)更先進(jìn)的策略。龐大的HDFS實(shí)例一般運(yùn)行在多個(gè)機(jī)架的計(jì)算機(jī)形成的集群上,不同機(jī)架間的兩臺(tái)機(jī)器的通訊需要通過(guò)交換機(jī),顯然通常情況下,同一個(gè)機(jī)架內(nèi)的兩個(gè)節(jié)點(diǎn)間的帶寬會(huì)比不同機(jī)架間的兩臺(tái)機(jī)器的帶寬大。
通過(guò)一個(gè)稱(chēng)為Rack Awareness的過(guò)程,Namenode決定了每個(gè)Datanode所屬的rack id。一個(gè)簡(jiǎn)單但沒(méi)有優(yōu)化的策略就是將副本存放在單獨(dú)的機(jī)架上。這樣可以防止整個(gè)機(jī)架(非副本存放)失效的情況,并且允許讀數(shù)據(jù)的時(shí)候可以從多個(gè)機(jī)架讀取。這個(gè)簡(jiǎn)單策略設(shè)置可以將副本分布在集群中,有利于組件失敗情況下的負(fù)載均衡。但是,這個(gè)簡(jiǎn)單策略加大了寫(xiě)的代價(jià),因?yàn)橐粋€(gè)寫(xiě)操作需要傳輸block到多個(gè)機(jī)架。
在大多數(shù)情況下,replication因子是3,HDFS的存放策略是將一個(gè)副本存放在本地機(jī)架上的節(jié)點(diǎn),一個(gè)副本放在同一機(jī)架上的另一個(gè)節(jié)點(diǎn),最后一個(gè)副本放在不同機(jī)架上的一個(gè)節(jié)點(diǎn)。機(jī)架的錯(cuò)誤遠(yuǎn)遠(yuǎn)比節(jié)點(diǎn)的錯(cuò)誤少,這個(gè)策略不會(huì)影響到數(shù)據(jù)的可靠性和有效性。三分之一的副本在一個(gè)節(jié)點(diǎn)上,三分之二在一個(gè)機(jī)架上,其他保存在剩下的機(jī)架中,這一策略改進(jìn)了寫(xiě)的性能。
2、副本的選擇,為了降低整體的帶寬消耗和讀延時(shí),HDFS會(huì)盡量讓reader讀最近的副本。如果在reader的同一個(gè)機(jī)架上有一個(gè)副本,那么就讀該副本。如果一個(gè)HDFS集群跨越多個(gè)數(shù)據(jù)中心,那么reader也將首先嘗試讀本地?cái)?shù)據(jù)中心的副本。
3、SafeMode
Namenode啟動(dòng)后會(huì)進(jìn)入一個(gè)稱(chēng)為SafeMode的特殊狀態(tài),處在這個(gè)狀態(tài)的Namenode是不會(huì)進(jìn)行數(shù)據(jù)塊的復(fù)制的。Namenode從所有的 Datanode接收心跳包和Blockreport。Blockreport包括了某個(gè)Datanode所有的數(shù)據(jù)塊列表。每個(gè)block都有指定的最小數(shù)目的副本。當(dāng)Namenode檢測(cè)確認(rèn)某個(gè)Datanode的數(shù)據(jù)塊副本的最小數(shù)目,那么該Datanode就會(huì)被認(rèn)為是安全的;如果一定百分比(這個(gè)參數(shù)可配置)的數(shù)據(jù)塊檢測(cè)確認(rèn)是安全的,那么Namenode將退出SafeMode狀態(tài),接下來(lái)它會(huì)確定還有哪些數(shù)據(jù)塊的副本沒(méi)有達(dá)到指定數(shù)目,并將這些block復(fù)制到其他Datanode。
五、文件系統(tǒng)元數(shù)據(jù)的持久化
Namenode存儲(chǔ)HDFS的元數(shù)據(jù)。對(duì)于任何對(duì)文件元數(shù)據(jù)產(chǎn)生修改的操作,Namenode都使用一個(gè)稱(chēng)為Editlog的事務(wù)日志記錄下來(lái)。例如,在HDFS中創(chuàng)建一個(gè)文件,Namenode就會(huì)在Editlog中插入一條記錄來(lái)表示;同樣,修改文件的replication因子也將往 Editlog插入一條記錄。Namenode在本地OS的文件系統(tǒng)中存儲(chǔ)這個(gè)Editlog。整個(gè)文件系統(tǒng)的namespace,包括block到文件的映射、文件的屬性,都存儲(chǔ)在稱(chēng)為FsImage的文件中,這個(gè)文件也是放在Namenode所在系統(tǒng)的文件系統(tǒng)上。
Namenode在內(nèi)存中保存著整個(gè)文件系統(tǒng)namespace和文件Blockmap的映像。這個(gè)關(guān)鍵的元數(shù)據(jù)設(shè)計(jì)得很緊湊,因而一個(gè)帶有4G內(nèi)存的 Namenode足夠支撐海量的文件和目錄。當(dāng)Namenode啟動(dòng)時(shí),它從硬盤(pán)中讀取Editlog和FsImage,將所有Editlog中的事務(wù)作用(apply)在內(nèi)存中的FsImage ,并將這個(gè)新版本的FsImage從內(nèi)存中flush到硬盤(pán)上,然后再truncate這個(gè)舊的Editlog,因?yàn)檫@個(gè)舊的Editlog的事務(wù)都已經(jīng)作用在FsImage上了。這個(gè)過(guò)程稱(chēng)為checkpoint。在當(dāng)前實(shí)現(xiàn)中,checkpoint只發(fā)生在Namenode啟動(dòng)時(shí),在不久的將來(lái)我們將實(shí)現(xiàn)支持周期性的checkpoint。
Datanode并不知道關(guān)于文件的任何東西,除了將文件中的數(shù)據(jù)保存在本地的文件系統(tǒng)上。它把每個(gè)HDFS數(shù)據(jù)塊存儲(chǔ)在本地文件系統(tǒng)上隔離的文件中。 Datanode并不在同一個(gè)目錄創(chuàng)建所有的文件,相反,它用啟發(fā)式地方法來(lái)確定每個(gè)目錄的最佳文件數(shù)目,并且在適當(dāng)?shù)臅r(shí)候創(chuàng)建子目錄。在同一個(gè)目錄創(chuàng)建所有的文件不是最優(yōu)的選擇,因?yàn)楸镜匚募到y(tǒng)可能無(wú)法高效地在單一目錄中支持大量的文件。當(dāng)一個(gè)Datanode啟動(dòng)時(shí),它掃描本地文件系統(tǒng),對(duì)這些本地文件產(chǎn)生相應(yīng)的一個(gè)所有HDFS數(shù)據(jù)塊的列表,然后發(fā)送報(bào)告到Namenode,這個(gè)報(bào)告就是Blockreport。
六、通訊協(xié)議
所有的HDFS通訊協(xié)議都是構(gòu)建在TCP/IP協(xié)議上。客戶(hù)端通過(guò)一個(gè)可配置的端口連接到Namenode,通過(guò)ClientProtocol與 Namenode交互。而Datanode是使用DatanodeProtocol與Namenode交互。從ClientProtocol和 Datanodeprotocol抽象出一個(gè)遠(yuǎn)程調(diào)用(RPC),在設(shè)計(jì)上,Namenode不會(huì)主動(dòng)發(fā)起RPC,而是是響應(yīng)來(lái)自客戶(hù)端和 Datanode 的RPC請(qǐng)求。
七、健壯性
HDFS的主要目標(biāo)就是實(shí)現(xiàn)在失敗情況下的數(shù)據(jù)存儲(chǔ)可靠性。常見(jiàn)的三種失敗:Namenode failures, Datanode failures和網(wǎng)絡(luò)分割(network partitions)。
1、硬盤(pán)數(shù)據(jù)錯(cuò)誤、心跳檢測(cè)和重新復(fù)制
每個(gè)Datanode節(jié)點(diǎn)都向Namenode周期性地發(fā)送心跳包。網(wǎng)絡(luò)切割可能導(dǎo)致一部分Datanode跟Namenode失去聯(lián)系。 Namenode通過(guò)心跳包的缺失檢測(cè)到這一情況,并將這些Datanode標(biāo)記為dead,不會(huì)將新的IO請(qǐng)求發(fā)給它們。寄存在dead Datanode上的任何數(shù)據(jù)將不再有效。Datanode的死亡可能引起一些block的副本數(shù)目低于指定值,Namenode不斷地跟蹤需要復(fù)制的 block,在任何需要的情況下啟動(dòng)復(fù)制。在下列情況可能需要重新復(fù)制:某個(gè)Datanode節(jié)點(diǎn)失效,某個(gè)副本遭到損壞,Datanode上的硬盤(pán)錯(cuò)誤,或者文件的replication因子增大。
2、集群均衡
HDFS支持?jǐn)?shù)據(jù)的均衡計(jì)劃,如果某個(gè)Datanode節(jié)點(diǎn)上的空閑空間低于特定的臨界點(diǎn),那么就會(huì)啟動(dòng)一個(gè)計(jì)劃自動(dòng)地將數(shù)據(jù)從一個(gè)Datanode搬移到空閑的Datanode。當(dāng)對(duì)某個(gè)文件的請(qǐng)求突然增加,那么也可能啟動(dòng)一個(gè)計(jì)劃創(chuàng)建該文件新的副本,并分布到集群中以滿(mǎn)足應(yīng)用的要求。這些均衡計(jì)劃目前還沒(méi)有實(shí)現(xiàn)。
3、數(shù)據(jù)完整性
從某個(gè)Datanode獲取的數(shù)據(jù)塊有可能是損壞的,這個(gè)損壞可能是由于Datanode的存儲(chǔ)設(shè)備錯(cuò)誤、網(wǎng)絡(luò)錯(cuò)誤或者軟件bug造成的。HDFS客戶(hù)端軟件實(shí)現(xiàn)了HDFS文件內(nèi)容的校驗(yàn)和。當(dāng)某個(gè)客戶(hù)端創(chuàng)建一個(gè)新的HDFS文件,會(huì)計(jì)算這個(gè)文件每個(gè)block的校驗(yàn)和,并作為一個(gè)單獨(dú)的隱藏文件保存這些校驗(yàn)和在同一個(gè)HDFS namespace下。當(dāng)客戶(hù)端檢索文件內(nèi)容,它會(huì)確認(rèn)從Datanode獲取的數(shù)據(jù)跟相應(yīng)的校驗(yàn)和文件中的校驗(yàn)和是否匹配,如果不匹配,客戶(hù)端可以選擇從其他Datanode獲取該block的副本。
4、元數(shù)據(jù)磁盤(pán)錯(cuò)誤
FsImage和Editlog是HDFS的核心數(shù)據(jù)結(jié)構(gòu)。這些文件如果損壞了,整個(gè)HDFS實(shí)例都將失效。因而,Namenode可以配置成支持維護(hù)多個(gè)FsImage和Editlog的拷貝。任何對(duì)FsImage或者Editlog的修改,都將同步到它們的副本上。這個(gè)同步操作可能會(huì)降低 Namenode每秒能支持處理的namespace事務(wù)。這個(gè)代價(jià)是可以接受的,因?yàn)?/span>HDFS是數(shù)據(jù)密集的,而非元數(shù)據(jù)密集。當(dāng)Namenode重啟的時(shí)候,它總是選取最近的一致的FsImage和Editlog使用。
Namenode在HDFS是單點(diǎn)存在,如果Namenode所在的機(jī)器錯(cuò)誤,手工的干預(yù)是必須的。目前,在另一臺(tái)機(jī)器上重啟因故障而停止服務(wù)的Namenode這個(gè)功能還沒(méi)實(shí)現(xiàn)。
5、快照
快照支持某個(gè)時(shí)間的數(shù)據(jù)拷貝,當(dāng)HDFS數(shù)據(jù)損壞的時(shí)候,可以恢復(fù)到過(guò)去一個(gè)已知正確的時(shí)間點(diǎn)。HDFS目前還不支持快照功能。
八、數(shù)據(jù)組織
1、數(shù)據(jù)塊
兼容HDFS的應(yīng)用都是處理大數(shù)據(jù)集合的。這些應(yīng)用都是寫(xiě)數(shù)據(jù)一次,讀卻是一次到多次,并且讀的速度要滿(mǎn)足流式讀。HDFS支持文件的write- once-read-many語(yǔ)義。一個(gè)典型的block大小是64MB,因而,文件總是按照64M切分成chunk,每個(gè)chunk存儲(chǔ)于不同的 Datanode
2、步驟
某個(gè)客戶(hù)端創(chuàng)建文件的請(qǐng)求其實(shí)并沒(méi)有立即發(fā)給Namenode,事實(shí)上,HDFS客戶(hù)端會(huì)將文件數(shù)據(jù)緩存到本地的一個(gè)臨時(shí)文件。應(yīng)用的寫(xiě)被透明地重定向到這個(gè)臨時(shí)文件。當(dāng)這個(gè)臨時(shí)文件累積的數(shù)據(jù)超過(guò)一個(gè)block的大小(默認(rèn)64M),客戶(hù)端才會(huì)聯(lián)系Namenode。Namenode將文件名插入文件系統(tǒng)的層次結(jié)構(gòu)中,并且分配一個(gè)數(shù)據(jù)塊給它,然后返回Datanode的標(biāo)識(shí)符和目標(biāo)數(shù)據(jù)塊給客戶(hù)端。客戶(hù)端將本地臨時(shí)文件flush到指定的 Datanode上。當(dāng)文件關(guān)閉時(shí),在臨時(shí)文件中剩余的沒(méi)有flush的數(shù)據(jù)也會(huì)傳輸?shù)街付ǖ?/span>Datanode,然后客戶(hù)端告訴Namenode文件已經(jīng)關(guān)閉。此時(shí)Namenode才將文件創(chuàng)建操作提交到持久存儲(chǔ)。如果Namenode在文件關(guān)閉前掛了,該文件將丟失。
上述方法是對(duì)通過(guò)對(duì)HDFS上運(yùn)行的目標(biāo)應(yīng)用認(rèn)真考慮的結(jié)果。如果不采用客戶(hù)端緩存,由于網(wǎng)絡(luò)速度和網(wǎng)絡(luò)堵塞會(huì)對(duì)吞估量造成比較大的影響。
3、流水線(xiàn)復(fù)制
當(dāng)某個(gè)客戶(hù)端向HDFS文件寫(xiě)數(shù)據(jù)的時(shí)候,一開(kāi)始是寫(xiě)入本地臨時(shí)文件,假設(shè)該文件的replication因子設(shè)置為3,那么客戶(hù)端會(huì)從Namenode 獲取一張Datanode列表來(lái)存放副本。然后客戶(hù)端開(kāi)始向第一個(gè)Datanode傳輸數(shù)據(jù),第一個(gè)Datanode一小部分一小部分(4kb)地接收數(shù)據(jù),將每個(gè)部分寫(xiě)入本地倉(cāng)庫(kù),并且同時(shí)傳輸該部分到第二個(gè)Datanode節(jié)點(diǎn)。第二個(gè)Datanode也是這樣,邊收邊傳,一小部分一小部分地收,存儲(chǔ)在本地倉(cāng)庫(kù),同時(shí)傳給第三個(gè)Datanode,第三個(gè)Datanode就僅僅是接收并存儲(chǔ)了。這就是流水線(xiàn)式的復(fù)制。
九、可訪(fǎng)問(wèn)性
HDFS給應(yīng)用提供了多種訪(fǎng)問(wèn)方式,可以通過(guò)DFSShell通過(guò)命令行與HDFS數(shù)據(jù)進(jìn)行交互,可以通過(guò)java API調(diào)用,也可以通過(guò)C語(yǔ)言的封裝API訪(fǎng)問(wèn),并且提供了瀏覽器訪(fǎng)問(wèn)的方式。正在開(kāi)發(fā)通過(guò)WebDav協(xié)議訪(fǎng)問(wèn)的方式。具體使用參考文檔。
十、空間的回收
1、文件的刪除和恢復(fù)
用戶(hù)或者應(yīng)用刪除某個(gè)文件,這個(gè)文件并沒(méi)有立刻從HDFS中刪除。相反,HDFS將這個(gè)文件重命名,并轉(zhuǎn)移到/trash目錄。當(dāng)文件還在/trash目錄時(shí),該文件可以被迅速地恢復(fù)。文件在/trash中保存的時(shí)間是可配置的,當(dāng)超過(guò)這個(gè)時(shí)間,Namenode就會(huì)將該文件從namespace中刪除。文件的刪除,也將釋放關(guān)聯(lián)該文件的數(shù)據(jù)塊。注意到,在文件被用戶(hù)刪除和HDFS空閑空間的增加之間會(huì)有一個(gè)等待時(shí)間延遲。
當(dāng)被刪除的文件還保留在/trash目錄中的時(shí)候,如果用戶(hù)想恢復(fù)這個(gè)文件,可以檢索瀏覽/trash目錄并檢索該文件。/trash目錄僅僅保存被刪除文件的最近一次拷貝。/trash目錄與其他文件目錄沒(méi)有什么不同,除了一點(diǎn):HDFS在該目錄上應(yīng)用了一個(gè)特殊的策略來(lái)自動(dòng)刪除文件,目前的默認(rèn)策略是刪除保留超過(guò)6小時(shí)的文件,這個(gè)策略以后會(huì)定義成可配置的接口。
2、Replication因子的減小
當(dāng)某個(gè)文件的replication因子減小,Namenode會(huì)選擇要?jiǎng)h除的過(guò)剩的副本。下次心跳檢測(cè)就將該信息傳遞給Datanode, Datanode就會(huì)移除相應(yīng)的block并釋放空間,同樣,在調(diào)用setReplication方法和集群中的空閑空間增加之間會(huì)有一個(gè)時(shí)間延遲。
參考資料:
HDFS Java API: http://hadoop.apache.org/core/docs/current/api/
HDFS source code: http://hadoop.apache.org/core/version_control.html
二. 分布式計(jì)算(Map/Reduce)
分布式式計(jì)算,同樣是一個(gè)寬泛的概念,在這里,它狹義的指代,按Google Map/Reduce框架所設(shè)計(jì)的分布式框架。在Hadoop中,分布式文件系統(tǒng),很大程度上,是為各種分布式計(jì)算需求所服務(wù)的。我們說(shuō)分布式文件系統(tǒng)就是加了分布式的文件系統(tǒng),類(lèi)似的定義推廣到分布式計(jì)算上,我們可以將其視為增加了分布式支持的計(jì)算函數(shù)。從計(jì)算的角度上看,Map/Reduce框架接受各種格式的鍵值對(duì)文件作為輸入,讀取計(jì)算后,最終生成自定義格式的輸出文件。而從分布式的角度上看,分布式計(jì)算的輸入文件往往規(guī)模巨大,且分布在多個(gè)機(jī)器上,單機(jī)計(jì)算完全不可支撐且效率低下,因此Map/Reduce框架需要提供一套機(jī)制,將此計(jì)算擴(kuò)展到無(wú)限規(guī)模的機(jī)器集群上進(jìn)行。依照這樣的定義,我們對(duì)整個(gè)Map/Reduce的理解,也可以分別沿著這兩個(gè)流程去看。。。
在Map/Reduce框架中,每一次計(jì)算請(qǐng)求,被稱(chēng)為作業(yè)。在分布式計(jì)算Map/Reduce框架中,為了完成這個(gè)作業(yè),它進(jìn)行兩步走的戰(zhàn)略,首先是將其拆分成若干個(gè)Map任務(wù),分配到不同的機(jī)器上去執(zhí)行,每一個(gè)Map任務(wù)拿輸入文件的一部分作為自己的輸入,經(jīng)過(guò)一些計(jì)算,生成某種格式的中間文件,這種格式,與最終所需的文件格式完全一致,但是僅僅包含一部分?jǐn)?shù)據(jù)。因此,等到所有Map任務(wù)完成后,它會(huì)進(jìn)入下一個(gè)步驟,用以合并這些中間文件獲得最后的輸出文件。此時(shí),系統(tǒng)會(huì)生成若干個(gè)Reduce任務(wù),同樣也是分配到不同的機(jī)器去執(zhí)行,它的目標(biāo),就是將若干個(gè)Map任務(wù)生成的中間文件為匯總到最后的輸出文件中去。當(dāng)然,這個(gè)匯總不總會(huì)像1 + 1 = 2那么直接了當(dāng),這也就是Reduce任務(wù)的價(jià)值所在。經(jīng)過(guò)如上步驟,最終,作業(yè)完成,所需的目標(biāo)文件生成。整個(gè)算法的關(guān)鍵,就在于增加了一個(gè)中間文件生成的流程,大大提高了靈活性,使其分布式擴(kuò)展性得到了保證。。。
I. 術(shù)語(yǔ)對(duì)照
和分布式文件系統(tǒng)一樣,Google、Hadoop和....我,各執(zhí)一種方式表述統(tǒng)一概念,為了保證其統(tǒng)一性,特有下表。。。
| 文中翻譯 |
Hadoop術(shù)語(yǔ) |
Google術(shù)語(yǔ) |
相關(guān)解釋 |
| 作業(yè) |
Job |
Job |
用戶(hù)的每一個(gè)計(jì)算請(qǐng)求,就稱(chēng)為一個(gè)作業(yè)。 |
| 作業(yè)服務(wù)器 |
JobTracker |
Master |
用戶(hù)提交作業(yè)的服務(wù)器,同時(shí),它還負(fù)責(zé)各個(gè)作業(yè)任務(wù)的分配,管理所有的任務(wù)服務(wù)器。 |
| 任務(wù)服務(wù)器 |
TaskTracker |
Worker |
任勞任怨的工蜂,負(fù)責(zé)執(zhí)行具體的任務(wù)。 |
| 任務(wù) |
Task |
Task |
每一個(gè)作業(yè),都需要拆分開(kāi)了,交由多個(gè)服務(wù)器來(lái)完成,拆分出來(lái)的執(zhí)行單位,就稱(chēng)為任務(wù)。 |
| 備份任務(wù) |
Speculative Task |
Buckup Task |
每一個(gè)任務(wù),都有可能執(zhí)行失敗或者緩慢,為了降低為此付出的代價(jià),系統(tǒng)會(huì)未雨綢繆的實(shí)現(xiàn)在另外的任務(wù)服務(wù)器上執(zhí)行同樣一個(gè)任務(wù),這就是備份任務(wù)。 |
II. 基本架構(gòu)
與分布式文件系統(tǒng)類(lèi)似,Map/Reduce的集群,也由三類(lèi)服務(wù)器構(gòu)成。其中作業(yè)服務(wù)器,在Hadoop中稱(chēng)為Job Tracker,在Google論文中稱(chēng)為Master。前者告訴我們,作業(yè)服務(wù)器是負(fù)責(zé)管理運(yùn)行在此框架下所有作業(yè)的,后者告訴我們,它也是為各個(gè)作業(yè)分配任務(wù)的核心。與HDFS的主控服務(wù)器類(lèi)似,它也是作為單點(diǎn)存在的,簡(jiǎn)化了負(fù)責(zé)的同步流程。具體的負(fù)責(zé)執(zhí)行用戶(hù)定義操作的,是任務(wù)服務(wù)器,每一個(gè)作業(yè)被拆分成很多的任務(wù),包括Map任務(wù)和Reduce任務(wù)等,任務(wù)是具體執(zhí)行的基本單元,它們都需要分配到合適任務(wù)服務(wù)器上去執(zhí)行,任務(wù)服務(wù)器一邊執(zhí)行一邊向作業(yè)服務(wù)器匯報(bào)各個(gè)任務(wù)的狀態(tài),以此來(lái)幫助作業(yè)服務(wù)器了解作業(yè)執(zhí)行的整體情況,分配新的任務(wù)等等。。。
除了作業(yè)的管理者執(zhí)行者,還需要有一個(gè)任務(wù)的提交者,這就是客戶(hù)端。與分布式文件系統(tǒng)一樣,客戶(hù)端也不是一個(gè)單獨(dú)的進(jìn)程,而是一組API,用戶(hù)需要自定義好自己需要的內(nèi)容,經(jīng)由客戶(hù)端相關(guān)的代碼,將作業(yè)及其相關(guān)內(nèi)容和配置,提交到作業(yè)服務(wù)器去,并時(shí)刻監(jiān)控執(zhí)行的狀況。。。
同作為Hadoop的實(shí)現(xiàn),與HDFS的通信機(jī)制相同,Hadoop Map/Reduce也是用了協(xié)議接口來(lái)進(jìn)行服務(wù)器間的交流。實(shí)現(xiàn)者作為RPC服務(wù)器,調(diào)用者經(jīng)由RPC的代理進(jìn)行調(diào)用,如此,完成大部分的通信,具體服務(wù)器的架構(gòu),和其中運(yùn)行的各個(gè)協(xié)議狀況,參見(jiàn)下圖。從圖中可以看到,與HDFS相比,相關(guān)的協(xié)議少了幾個(gè),客戶(hù)端與任務(wù)服務(wù)器,任務(wù)服務(wù)器之間,都不再有直接通信關(guān)系。這并不意味著客戶(hù)端就不需要了解具體任務(wù)的執(zhí)行狀況,也不意味著,任務(wù)服務(wù)器之間不需要了解別家任務(wù)執(zhí)行的情形,只不過(guò),由于整個(gè)集群各機(jī)器的聯(lián)系比HDFS復(fù)雜的多,直接通信過(guò)于的難以維系,所以,都統(tǒng)一由作業(yè)服務(wù)器整理轉(zhuǎn)發(fā)。另外,從這幅圖可以看到,任務(wù)服務(wù)器不是一個(gè)人在戰(zhàn)斗,它會(huì)像孫悟空一樣招出一群寶寶幫助其具體執(zhí)行任務(wù)。這樣做的好處,個(gè)人覺(jué)得,應(yīng)該有安全性方面的考慮,畢竟,任務(wù)的代碼是用戶(hù)提交的,數(shù)據(jù)也是用戶(hù)指定的,這質(zhì)量自然良莠不齊,萬(wàn)一碰上個(gè)搞破壞的,把整個(gè)任務(wù)服務(wù)器進(jìn)程搞死了,就因小失大了。因此,放在單獨(dú)的地盤(pán)進(jìn)行,愛(ài)咋咋地,也算是權(quán)責(zé)明確了。。。
與分布式文件系統(tǒng)相比,Map/Reduce框架的還有一個(gè)特點(diǎn),就是可定制性強(qiáng)。文件系統(tǒng)中很多的算法,都是很固定和直觀(guān)的,不會(huì)由于所存儲(chǔ)的內(nèi)容不同而有太多的變化。而作為通用的計(jì)算框架,需要面對(duì)的問(wèn)題則要復(fù)雜很多,在各種不同的問(wèn)題、不同的輸入、不同的需求之間,很難有一種包治百病的藥能夠一招鮮吃遍天。作為Map/Reduce框架而言,一方面要盡可能的抽取出公共的一些需求,實(shí)現(xiàn)出來(lái)。更重要的,是需要提供良好的可擴(kuò)展機(jī)制,滿(mǎn)足用戶(hù)自定義各種算法的需求。Hadoop是由Java來(lái)實(shí)現(xiàn)的,因此通過(guò)反射來(lái)實(shí)現(xiàn)自定義的擴(kuò)展,顯得比較小菜一碟了。在JobConf類(lèi)中,定義了大量的接口,這基本上是Hadoop Map/Reduce框架所有可定制內(nèi)容的一次集中展示。在JobConf中,有大量set接口接受一個(gè)Class<? extends xxx>的參數(shù),通常它都有一個(gè)默認(rèn)實(shí)現(xiàn)的類(lèi),用戶(hù)如果不滿(mǎn)意,則可自定義實(shí)現(xiàn)。。。
III. 計(jì)算流程
如果一切都按部就班的進(jìn)行,那么整個(gè)作業(yè)的計(jì)算流程,應(yīng)該是作業(yè)的提交 -> Map任務(wù)的分配和執(zhí)行 -> Reduce任務(wù)的分配和執(zhí)行 -> 作業(yè)的完成。而在每個(gè)任務(wù)的執(zhí)行中,又包含輸入的準(zhǔn)備 -> 算法的執(zhí)行 -> 輸出的生成,三個(gè)子步驟。沿著這個(gè)流程,我們可以很快的整理清晰整個(gè)Map/Reduce框架下作業(yè)的執(zhí)行。。。
1、作業(yè)的提交
一個(gè)作業(yè),在提交之前,需要把所有應(yīng)該配置的東西都配置好,因?yàn)橐坏┨峤坏搅俗鳂I(yè)服務(wù)器上,就陷入了完全自動(dòng)化的流程,用戶(hù)除了觀(guān)望,最多也就能起一個(gè)監(jiān)督作用,懲治一些不好好工作的任務(wù)。。。
基本上,用戶(hù)在提交代碼階段,需要做的工作主要是這樣的:
首先,書(shū)寫(xiě)好所有自定的代碼,最起碼,需要有Map和Reduce的執(zhí)行代碼。在Hadoop中,Map需要派生自Mapper<K1, V1, K2, V2>接口,Reduce需要派生自Reducer<K2, V2, K3, V3>接口。這里都是用的泛型,用以支持不同的鍵值類(lèi)型。這兩個(gè)接口都僅有一個(gè)方法,一個(gè)是map,一個(gè)是reduce,這兩個(gè)方法都直接受四個(gè)參數(shù),前兩個(gè)是輸入的鍵和值相關(guān)的數(shù)據(jù)結(jié)構(gòu),第三個(gè)是作為輸出相關(guān)的數(shù)據(jù)結(jié)構(gòu),最后一個(gè),是一個(gè)Reporter類(lèi)的實(shí)例,實(shí)現(xiàn)的時(shí)候可以利用它來(lái)統(tǒng)計(jì)一些計(jì)數(shù)。除了這兩個(gè)接口,還有大量可以派生的接口,比如分割的Partitioner<K2, V2>接口。。。
然后,需要書(shū)寫(xiě)好主函數(shù)的代碼,其中最主要的內(nèi)容就是實(shí)例化一個(gè)JobConf類(lèi)的對(duì)象,然后調(diào)用其豐富的setXXX接口,設(shè)定好所需的內(nèi)容,包括輸入輸出的文件路徑,Map和Reduce的類(lèi),甚至包括讀取寫(xiě)入文件所需的格式支持類(lèi),等等。。。
最后,調(diào)用JobClient的runJob方法,提交此JobConf對(duì)象。runJob方法會(huì)先行調(diào)用到JobSubmissionProtocol接口所定義的submitJob方法,將此作業(yè),提交給作業(yè)服務(wù)器。接著,runJob開(kāi)始循環(huán),不停的調(diào)用JobSubmissionProtocol的getTaskCompletionEvents方法,獲得TaskCompletionEvent類(lèi)的對(duì)象實(shí)例,了解此作業(yè)各任務(wù)的執(zhí)行狀況。。。
2、Map任務(wù)的分配
當(dāng)一個(gè)作業(yè)提交到了作業(yè)服務(wù)器上,作業(yè)服務(wù)器會(huì)生成若干個(gè)Map任務(wù),每一個(gè)Map任務(wù),負(fù)責(zé)將一部分的輸入轉(zhuǎn)換成格式與最終格式相同的中間文件。通常一個(gè)作業(yè)的輸入都是基于分布式文件系統(tǒng)的文件(當(dāng)然在單機(jī)環(huán)境下,文件系統(tǒng)單機(jī)的也可以...),因?yàn)椋梢院芴烊坏暮头植际降挠?jì)算產(chǎn)生聯(lián)系。而對(duì)于一個(gè)Map任務(wù)而言,它的輸入往往是輸入文件的一個(gè)數(shù)據(jù)塊,或者是數(shù)據(jù)塊的一部分,但通常,不跨數(shù)據(jù)塊。因?yàn)椋坏┛缌藬?shù)據(jù)塊,就可能涉及到多個(gè)服務(wù)器,帶來(lái)了不必要的復(fù)雜性。。。
當(dāng)一個(gè)作業(yè),從客戶(hù)端提交到了作業(yè)服務(wù)器上,作業(yè)服務(wù)器會(huì)生成一個(gè)JobInProgress對(duì)象,作為與之對(duì)應(yīng)的標(biāo)識(shí),用于管理。作業(yè)被拆分成若干個(gè)Map任務(wù)后,會(huì)預(yù)先掛在作業(yè)服務(wù)器上的任務(wù)服務(wù)器拓?fù)錁?shù)。這是依照分布式文件數(shù)據(jù)塊的位置來(lái)劃分的,比如一個(gè)Map任務(wù)需要用某個(gè)數(shù)據(jù)塊,這個(gè)數(shù)據(jù)塊有三份備份,那么,在這三臺(tái)服務(wù)器上都會(huì)掛上此任務(wù),可以視為是一個(gè)預(yù)分配。。。
關(guān)于任務(wù)管理和分配的大部分的真實(shí)功能和邏輯的實(shí)現(xiàn),JobInProgress則依托JobInProgressListener和TaskScheduler的子類(lèi)。TaskScheduler,顧名思義是用于任務(wù)分配的策略類(lèi)(為了簡(jiǎn)化描述,用它代指所有TaskScheduler的子類(lèi)...)。它會(huì)掌握好所有作業(yè)的任務(wù)信息,其assignTasks函數(shù),接受一個(gè)TaskTrackerStatus作為參數(shù),依照此任務(wù)服務(wù)器的狀態(tài)和現(xiàn)有的任務(wù)狀況,為其分配新的任務(wù)。而為了掌握所有作業(yè)相關(guān)任務(wù)的狀況,TaskScheduler會(huì)將若干個(gè)JobInProgressListener注冊(cè)到JobTracker中去,當(dāng)有新的作業(yè)到達(dá)、移除或更新的時(shí)候,JobTracker會(huì)告知給所有的JobInProgressListener,以便它們做出相應(yīng)的處理。。。
任務(wù)分配是一個(gè)重要的環(huán)節(jié),所謂任務(wù)分配,就是將合適作業(yè)的合適任務(wù)分配到合適的服務(wù)器上。不難看出,里面蘊(yùn)含了兩個(gè)步驟,先是選擇作業(yè),然后是在此作業(yè)中選擇任務(wù)。和所有分配工作一樣,任務(wù)分配也是一個(gè)復(fù)雜的活。不良好的任務(wù)分配,可能會(huì)導(dǎo)致網(wǎng)絡(luò)流量增加、某些任務(wù)服務(wù)器負(fù)載過(guò)重效率下降,等等。不僅如此,任務(wù)分配還是一個(gè)無(wú)一致模式的問(wèn)題,不同的業(yè)務(wù)背景,可能需要不同的算法才能滿(mǎn)足需求。因此,在Hadoop中,有很多TaskScheduler的子類(lèi),像Facebook,Yahoo,都為其貢獻(xiàn)出了自家用的算法。在Hadoop中,默認(rèn)的任務(wù)分配器,是JobQueueTaskScheduler類(lèi)。它選擇作業(yè)的基本次序是:Map Clean Up Task(Map任務(wù)服務(wù)器的清理任務(wù),用于清理相關(guān)的過(guò)期的文件和環(huán)境...) -> Map Setup Task(Map任務(wù)服務(wù)器的安裝任務(wù),負(fù)責(zé)配置好相關(guān)的環(huán)境...) -> Map Tasks -> Reduce Clean Up Task -> Reduce Setup Task -> Reduce Tasks。在這個(gè)前提下,具體到Map任務(wù)的分配上來(lái)。當(dāng)一個(gè)任務(wù)服務(wù)器工作的游刃有余,期待獲得新的任務(wù)的時(shí)候,JobQueueTaskScheduler會(huì)按照各個(gè)作業(yè)的優(yōu)先級(jí),從最高優(yōu)先級(jí)的作業(yè)開(kāi)始分配。每分配一個(gè),還會(huì)為其留出余量,已被不時(shí)之需。舉一個(gè)例子:系統(tǒng)目前有優(yōu)先級(jí)3、2、1的三個(gè)作業(yè),每個(gè)作業(yè)都有一個(gè)可分配的Map任務(wù),一個(gè)任務(wù)服務(wù)器來(lái)申請(qǐng)新的任務(wù),它還有能力承載3個(gè)任務(wù)的執(zhí)行,JobQueueTaskScheduler會(huì)先從優(yōu)先級(jí)3的作業(yè)上取一個(gè)任務(wù)分配給它,然后再留出一個(gè)1任務(wù)的余量。此時(shí),系統(tǒng)只能在將優(yōu)先級(jí)2作業(yè)的任務(wù)分配給此服務(wù)器,而不能分配優(yōu)先級(jí)1的任務(wù)。這樣的策略,基本思路就是一切為高優(yōu)先級(jí)的作業(yè)服務(wù),優(yōu)先分配不說(shuō),分配了好保留有余力以備不時(shí)之需,如此優(yōu)待,足以讓高優(yōu)先級(jí)的作業(yè)喜極而泣,讓低優(yōu)先級(jí)的作業(yè)感慨既生瑜何生亮,甚至是活活餓死。。。
確定了從哪個(gè)作業(yè)提取任務(wù)后,具體的分配算法,經(jīng)過(guò)一系列的調(diào)用,最后實(shí)際是由JobInProgress的findNewMapTask函數(shù)完成的。它的算法很簡(jiǎn)單,就是盡全力為此服務(wù)器非配且盡可能好的分配任務(wù),也就是說(shuō),只要還有可分配的任務(wù),就一定會(huì)分給它,而不考慮后來(lái)者。作業(yè)服務(wù)器會(huì)從離它最近的服務(wù)器開(kāi)始,看上面是否還掛著未分配的任務(wù)(預(yù)分配上的),從近到遠(yuǎn),如果所有的任務(wù)都分配了,那么看有沒(méi)有開(kāi)啟多次執(zhí)行,如果開(kāi)啟,考慮把未完成的任務(wù)再分配一次(后面有地方詳述...)。。。
對(duì)于作業(yè)服務(wù)器來(lái)說(shuō),把一個(gè)任務(wù)分配出去了,并不意味著它就徹底解放,可以對(duì)此任務(wù)可以不管不顧了。因?yàn)槿蝿?wù)可以在任務(wù)服務(wù)器上執(zhí)行失敗,可能執(zhí)行緩慢,這都需要作業(yè)服務(wù)器幫助它們?cè)賮?lái)一次。因此在Task中,記錄有一個(gè)TaskAttemptID,對(duì)于任務(wù)服務(wù)器而言,它們每次跑的,其實(shí)都只是一個(gè)Attempt而已,Reduce任務(wù)只需要采信一個(gè)的輸出,其他都算白忙乎了。。。
3、Map任務(wù)的執(zhí)行
與HDFS類(lèi)似,任務(wù)服務(wù)器是通過(guò)心跳消息,向作業(yè)服務(wù)器匯報(bào)此時(shí)此刻其上各個(gè)任務(wù)執(zhí)行的狀況,并向作業(yè)服務(wù)器申請(qǐng)新的任務(wù)的。具體實(shí)現(xiàn),是TaskTracker調(diào)用InterTrackerProtocol協(xié)議的heartbeat方法來(lái)做的。這個(gè)方法接受一個(gè)TaskTrackerStatus對(duì)象作為參數(shù),它描述了此時(shí)此任務(wù)服務(wù)器的狀態(tài)。當(dāng)其有余力接受新的任務(wù)的時(shí)候,它還會(huì)傳入acceptNewTasks為true的參數(shù),表示希望作業(yè)服務(wù)器委以重任。JobTracker接收到相關(guān)的參數(shù)后,經(jīng)過(guò)處理,會(huì)返回一個(gè)HeartbeatResponse對(duì)象。這個(gè)對(duì)象中,定義了一組TaskTrackerAction,用于指導(dǎo)任務(wù)服務(wù)器進(jìn)行下一步的工作。系統(tǒng)中已定義的了一堆其TaskTrackerAction的子類(lèi),有的對(duì)攜帶的參數(shù)進(jìn)行了擴(kuò)充,有的只是標(biāo)明了下ID,具體不詳寫(xiě)了,一看便知。。。
當(dāng)TaskTracker收到的TaskTrackerAction中,包含了LaunchTaskAction,它會(huì)開(kāi)始執(zhí)行所分配的新的任務(wù)。在TaskTracker中,有一個(gè)TaskTracker.TaskLauncher線(xiàn)程(確切的說(shuō)是兩個(gè),一個(gè)等Map任務(wù),一個(gè)等Reduce任務(wù)),它們?cè)诎V癡的守候著新任務(wù)的來(lái)到。一旦等到了,會(huì)最終調(diào)用到Task的createRunner方法,構(gòu)造出一個(gè)TaskRunner對(duì)象,新建一個(gè)線(xiàn)程來(lái)執(zhí)行。對(duì)于一個(gè)Map任務(wù),它對(duì)應(yīng)的Runner是TaskRunner的子類(lèi)MapTaskRunner,不過(guò),核心部分都在TaskRunner的實(shí)現(xiàn)內(nèi)。TaskRunner會(huì)先將所需的文件全部下載并拆包好,并記錄到一個(gè)全局緩存中,這是一個(gè)全局的目錄,可以供所有此作業(yè)的所有任務(wù)使用。它會(huì)用一些軟鏈接,將一些文件名鏈接到這個(gè)緩存中來(lái)。然后,根據(jù)不同的參數(shù),配置出一個(gè)JVM執(zhí)行的環(huán)境,這個(gè)環(huán)境與JvmEnv類(lèi)的對(duì)象對(duì)應(yīng)。
接著,TaskRunner會(huì)調(diào)用JvmManager的launchJvm方法,提交給JvmManager處理。JvmManager用于管理該TaskTracker上所有運(yùn)行的Task子進(jìn)程。在目前的實(shí)現(xiàn)中,嘗試的是池化的方式。有若干個(gè)固定的槽,如果槽沒(méi)有滿(mǎn),那么就啟動(dòng)新的子進(jìn)程,否則,就尋找idle的進(jìn)程,如果是同Job的直接放進(jìn)去,否則殺死這個(gè)進(jìn)程,用一個(gè)新的進(jìn)程代替。每一個(gè)進(jìn)程都是由JvmRunner來(lái)管理的,它也是位于單獨(dú)線(xiàn)程中的。但是從實(shí)現(xiàn)上看,這個(gè)機(jī)制好像沒(méi)有部署開(kāi),子進(jìn)程是死循環(huán)等待,而不會(huì)阻塞在父進(jìn)程的相關(guān)線(xiàn)程上,父線(xiàn)程的變量一直都沒(méi)有個(gè)調(diào)整,一旦分配,始終都處在繁忙的狀況了。
真實(shí)的執(zhí)行載體,是Child,它包含一個(gè)main函數(shù),進(jìn)程執(zhí)行,會(huì)將相關(guān)參數(shù)傳進(jìn)來(lái),它會(huì)拆解這些參數(shù),并且構(gòu)造出相關(guān)的Task實(shí)例,調(diào)用其run函數(shù)進(jìn)行執(zhí)行。每一個(gè)子進(jìn)程,可以執(zhí)行指定個(gè)數(shù)量的Task,這就是上面所說(shuō)的池化的配置。但是,這套機(jī)制在我看來(lái),并沒(méi)有運(yùn)行起來(lái),每個(gè)進(jìn)程其實(shí)都沒(méi)有機(jī)會(huì)不死而執(zhí)行新的任務(wù),只是傻傻的等待進(jìn)程池滿(mǎn),而被一刀斃命。也許是我老眼昏花,沒(méi)看出其中實(shí)現(xiàn)的端倪。。。
4、Reduce任務(wù)的分配與執(zhí)行
比之Map任務(wù),Reduce的分配及其簡(jiǎn)單,基本上是所有Map任務(wù)完成了,有空閑的任務(wù)服務(wù)器,來(lái)了就給分配一個(gè)Job任務(wù)。因?yàn)镸ap任務(wù)的結(jié)果星羅棋布,且變化多端,真要搞一個(gè)全局優(yōu)化的算法,絕對(duì)是得不償失。而Reduce任務(wù)的執(zhí)行進(jìn)程的構(gòu)造和分配流程,與Map基本完全的一致,沒(méi)有啥可說(shuō)的了。。。
但其實(shí),Reduce任務(wù)與Map任務(wù)的最大不同,是Map任務(wù)的文件都在本地隔著,而Reduce任務(wù)需要到處采集。這個(gè)流程是作業(yè)服務(wù)器經(jīng)由此Reduce任務(wù)所處的任務(wù)服務(wù)器,告訴Reduce任務(wù)正在執(zhí)行的進(jìn)程,它需要的Map任務(wù)執(zhí)行過(guò)的服務(wù)器地址,此Reduce任務(wù)服務(wù)器會(huì)于原Map任務(wù)服務(wù)器聯(lián)系(當(dāng)然本地就免了...),通過(guò)FTP服務(wù),下載過(guò)來(lái)。這個(gè)隱含的直接數(shù)據(jù)聯(lián)系,就是執(zhí)行Reduce任務(wù)與執(zhí)行Map任務(wù)最大的不同了。。。
5、作業(yè)的完成
當(dāng)所有Reduce任務(wù)都完成了,所需數(shù)據(jù)都寫(xiě)到了分布式文件系統(tǒng)上,整個(gè)作業(yè)才正式完成了。此中,涉及到很多的類(lèi),很多的文件,很多的服務(wù)器,所以說(shuō)起來(lái)很費(fèi)勁,話(huà)說(shuō),一圖解千語(yǔ),說(shuō)了那么多,我還是畫(huà)兩幅圖,徹底表達(dá)一下吧。。。
首先,是一個(gè)時(shí)序圖。它模擬了一個(gè)由3個(gè)Map任務(wù)和1個(gè)Reduce任務(wù)構(gòu)成的作業(yè)執(zhí)行流程。我們可以看到,在執(zhí)行的過(guò)程中,只要有人太慢,或者失敗,就會(huì)增加一次嘗試,以此換取最快的執(zhí)行總時(shí)間。一旦所有Map任務(wù)完成,Reduce開(kāi)始運(yùn)作(其實(shí),不一定要這樣的...),對(duì)于每一個(gè)Map任務(wù)來(lái)說(shuō),只有執(zhí)行到Reduce任務(wù)把它上面的數(shù)據(jù)下載完成,才算成功,否則,都是失敗,需要重新進(jìn)行嘗試。。。
而第二副圖,不是我畫(huà)的,就不轉(zhuǎn)載了,參見(jiàn)
這里,它描述了整個(gè)Map/Reduce的服務(wù)器狀況圖,包括整體流程、所處服務(wù)器進(jìn)程、輸入輸出等,看清楚這幅圖,對(duì)Map/Reduce的基本流程應(yīng)該能完全跑通了。有這幾點(diǎn),可能圖中描述的不夠清晰需要提及一下,一個(gè)是在HDFS中,其實(shí)還有日志文件,圖中沒(méi)有標(biāo)明;另一個(gè)是步驟5,其實(shí)是由TaskTracker主動(dòng)去拉取而不是JobTracker推送過(guò)來(lái)的;還有步驟8和步驟11,創(chuàng)建出來(lái)的MapTask和ReduceTask,在Hadoop中都是運(yùn)行在獨(dú)立的進(jìn)程上的。。。
IV. Map任務(wù)詳請(qǐng)
從上面,可以了解到整個(gè)Map和Reduce任務(wù)的整體流程,而后面要啰嗦的,是具體執(zhí)行中的細(xì)節(jié)。Map任務(wù)的輸入,是分布式文件系統(tǒng)上的,包含鍵值對(duì)信息的文件。為了給每一個(gè)Map任務(wù)指定輸入,我們需要掌握文件格式把它分切成塊,并從每一塊中分離出鍵值信息。在HDFS中,輸入的文件格式,是由InputFormat<K, V>類(lèi)來(lái)表示的,在JobConf中,它的默認(rèn)值是TextInputFormat類(lèi)(見(jiàn)getInputFormat),此類(lèi)是特化的FileInputFormat<LongWritable, Text>子類(lèi),而FileInputFormat<K, V>正是InputFormat<K, V>的子類(lèi)。通過(guò)這樣的關(guān)系我們可以很容易的理解,默認(rèn)的文件格式是文本文件,且鍵是LongWritable類(lèi)型(整形數(shù)),值是Text類(lèi)型(字符串)。僅僅知道文件類(lèi)型是不夠的,我們還需要將文件中的每一條數(shù)據(jù),分離成鍵值對(duì),這個(gè)工作,是RecordReader<K, V>來(lái)做的。在TextInputFormat的getRecordReader方法中我們可以看到,與TextInputFormat默認(rèn)配套使用的,是LineRecordReader類(lèi),是特化的RecordReader<LongWritable, Text>的子類(lèi),它將每一行作為一個(gè)記錄,起始的位置作為鍵,整行的字符串作為值。有了格式,分出了鍵值,還需要切開(kāi)分給每一個(gè)Map任務(wù)。每一個(gè)Map任務(wù)的輸入用InputSplit接口表示,對(duì)于一個(gè)文件輸入而言,其實(shí)現(xiàn)是FileSplit,它包含著文件名、起始位置、長(zhǎng)度和存儲(chǔ)它的一組服務(wù)器地址。。。
當(dāng)Map任務(wù)拿到所屬的InputSplit后,就開(kāi)始一條條讀取記錄,并調(diào)用用于定義的Mapper,進(jìn)行計(jì)算(參見(jiàn)MapRunner<K1, V1, K2, V2>和MapTask的run方法),然后,輸出。MapTask會(huì)傳遞給Mapper一個(gè)OutputCollector<K, V>對(duì)象,作為輸出的數(shù)據(jù)結(jié)構(gòu)。它定義了一個(gè)collect的函數(shù),接受一個(gè)鍵值對(duì)。在MapTask中,定義了兩個(gè)OutputCollector的子類(lèi),一個(gè)是MapTask.DirectMapOutputCollector<K, V>,人如其名,它的實(shí)現(xiàn)確實(shí)很Direct,直截了當(dāng)。它會(huì)利用一個(gè)RecordWriter<K, V>對(duì)象,collect一調(diào)用,就直接調(diào)用RecordWriter<K, V>的write方法,寫(xiě)入本地的文件中去。如果覺(jué)著RecordWriter<K, V>出現(xiàn)的很突兀,那么看看上一段提到的RecordReader<K, V>,基本上,數(shù)據(jù)結(jié)構(gòu)都是對(duì)應(yīng)著的,一個(gè)是輸入一個(gè)是輸出。輸出很對(duì)稱(chēng)也是由RecordWriter<K, V>和OutputFormat<K, V>來(lái)協(xié)同完成的,其默認(rèn)實(shí)現(xiàn)是LineRecordWriter<K, V>和TextOutputFormat<K, V>,多么的眼熟啊。。。
除了這個(gè)非常直接的實(shí)現(xiàn)之外,MapTask中還有一個(gè)復(fù)雜的多的實(shí)現(xiàn),是MapTask.MapOutputBuffer<K extends Object, V extends Object>。有道是簡(jiǎn)單壓倒一切,那為什么有很簡(jiǎn)單的實(shí)現(xiàn),要琢磨一個(gè)復(fù)雜的呢。原因在于,看上去很美的往往帶著刺,簡(jiǎn)單的輸出實(shí)現(xiàn),每調(diào)用一次collect就寫(xiě)一次文件,頻繁的硬盤(pán)操作很有可能導(dǎo)致此方案的低效。為了解決這個(gè)問(wèn)題,這就有了這個(gè)復(fù)雜版本,它先開(kāi)好一段內(nèi)存做緩存,然后制定一個(gè)比例做閾值,開(kāi)一個(gè)線(xiàn)程監(jiān)控此緩存。collect來(lái)的內(nèi)容,先寫(xiě)到緩存中,當(dāng)監(jiān)控線(xiàn)程發(fā)現(xiàn)緩存的內(nèi)容比例超過(guò)閾值,掛起所有寫(xiě)入操作,建一個(gè)新的文件,把緩存的內(nèi)容批量刷到此文件中去,清空緩存,重新開(kāi)放,接受繼續(xù)collect。。。
為什么說(shuō)是刷到文件中去呢。因?yàn)檫@不是一個(gè)簡(jiǎn)單的照本宣科簡(jiǎn)單復(fù)制的過(guò)程,在寫(xiě)入之前,會(huì)先將緩存中的內(nèi)存,經(jīng)過(guò)排序、合并器(Combiner)統(tǒng)計(jì)之后,才會(huì)寫(xiě)入。如果你覺(jué)得Combiner這個(gè)名詞聽(tīng)著太陌生,那么考慮一下Reducer,Combiner也就是一個(gè)Reducer類(lèi),通過(guò)JobConf的setCombinerClass進(jìn)行設(shè)置,在常用的配置中,Combiner往往就是用用戶(hù)為Reduce任務(wù)定義的那個(gè)Reducer子類(lèi)。只不過(guò),Combiner只是服務(wù)的范圍更小一些而已,它在Map任務(wù)執(zhí)行的服務(wù)器本地,依照Map處理過(guò)的那一小部分?jǐn)?shù)據(jù),先做一次Reduce操作,這樣,可以壓縮需要傳輸內(nèi)容的大小,提高速度。每一次刷緩存,都會(huì)開(kāi)一個(gè)新的文件,等此任務(wù)所有的輸入都處理完成后,就有了若干個(gè)有序的、經(jīng)過(guò)合并的輸出文件。系統(tǒng)會(huì)將這些文件搞在一起,再做一個(gè)多路的歸并外排,同時(shí)使用合并器進(jìn)行合并,最終,得到了唯一的、有序的、經(jīng)過(guò)合并的中間文件(注:文件數(shù)量等同于分類(lèi)數(shù)量,在不考慮分類(lèi)的時(shí)候,簡(jiǎn)單的視為一個(gè)...)。它,就是Reduce任務(wù)夢(mèng)寐以求的輸入文件。。。
除了做合并,復(fù)雜版本的OutputCollector,還具有分類(lèi)的功能。分類(lèi),是通過(guò)Partitioner<K2, V2>來(lái)定義的,默認(rèn)實(shí)現(xiàn)是HashPartitioner<K2, V2>,作業(yè)提交者可以通過(guò)JobConf的setPartitionerClass來(lái)自定義。分類(lèi)的含義是什么呢,簡(jiǎn)單的說(shuō),就是將Map任務(wù)的輸出,劃分到若干個(gè)文件中(通常與Reduce任務(wù)數(shù)目相等),使得每一個(gè)Reduce任務(wù),可以處理某一類(lèi)文件。這樣的好處是大大的,舉一個(gè)例子說(shuō)明一下。比如有一個(gè)作業(yè)是進(jìn)行單詞統(tǒng)計(jì)的,其Map任務(wù)的中間結(jié)果應(yīng)該是以單詞為鍵,以單詞數(shù)量為值的文件。如果這時(shí)候只有一個(gè)Reduce任務(wù),那還好說(shuō),從全部的Map任務(wù)那里收集文件過(guò)來(lái),分別統(tǒng)計(jì)得到最后的輸出文件就好。但是,如果單Reduce任務(wù)無(wú)法承載此負(fù)載或效率太低,就需要多個(gè)Reduce任務(wù)并行執(zhí)行。此時(shí),再沿用之前的模式就有了問(wèn)題。每個(gè)Reduce任務(wù)從一部分Map任務(wù)那里獲得輸入文件,但最終的輸出結(jié)果并不正確,因?yàn)橥粋€(gè)單詞可能在不同的Reduce任務(wù)那里都有統(tǒng)計(jì),需要想方法把它們統(tǒng)計(jì)在一起才能獲得最后結(jié)果,這樣就沒(méi)有將Map/Reduce的作用完全發(fā)揮出來(lái)。這時(shí)候,就需要用到分類(lèi)。如果此時(shí)有兩個(gè)Reduce任務(wù),那么將輸出分成兩類(lèi),一類(lèi)存放字母表排序較高的單詞,一類(lèi)存放字母表排序低的單詞,每一個(gè)Reduce任務(wù)從所有的Map任務(wù)那里獲取一類(lèi)的中間文件,得到自己的輸出結(jié)果。最終的結(jié)果,只需要把各個(gè)Reduce任務(wù)輸出的,拼接在一起就可以了。本質(zhì)上,這就是將Reduce任務(wù)的輸入,由垂直分割,變成了水平分割。Partitioner的作用,正是接受一個(gè)鍵值,返回一個(gè)分類(lèi)的序號(hào)。它會(huì)在從緩存刷到文件之前做這個(gè)工作,其實(shí)只是多了一個(gè)文件名的選擇而已,別的邏輯都不需要變化。。。
除了緩存、合并、分類(lèi)等附加工作之外,復(fù)雜版本的OutputCollector還支持錯(cuò)誤數(shù)據(jù)的跳過(guò)功能,在后面分布式將排錯(cuò)的時(shí)候,還會(huì)提及,標(biāo)記一下,按下不表。。。
V. Reduce任務(wù)詳情
理論上看,Reduce任務(wù)的整個(gè)執(zhí)行流程要比Map任務(wù)更為的羅嗦一些,因?yàn)椋枰占斎胛募缓蟛拍苓M(jìn)行處理。Reduce任務(wù),主要有這么三個(gè)步驟:Copy、Sort、Reduce(參見(jiàn)ReduceTask的run方法)。所謂Copy,就是從執(zhí)行各個(gè)Map任務(wù)的服務(wù)器那里,收羅到本地來(lái)。拷貝的任務(wù),是由ReduceTask.ReduceCopier類(lèi)來(lái)負(fù)責(zé),它有一個(gè)內(nèi)嵌類(lèi),叫MapOutputCopier,它會(huì)在一個(gè)單獨(dú)的線(xiàn)程內(nèi),負(fù)責(zé)某個(gè)Map任務(wù)服務(wù)器上文件的拷貝工作。遠(yuǎn)程拷貝過(guò)來(lái)的內(nèi)容(當(dāng)然也可以是本地了...),作為MapOutput對(duì)象存在,它可以在內(nèi)存中也可以序列化在磁盤(pán)上,這個(gè)根據(jù)內(nèi)存使用狀況來(lái)自動(dòng)調(diào)節(jié)。整個(gè)拷貝過(guò)程是一個(gè)動(dòng)態(tài)的過(guò)程,也就是說(shuō)它不是一次給好所有輸入信息就不再變化了。它會(huì)不停的調(diào)用TaskUmbilicalProtocol協(xié)議的getMapCompletionEvents方法,向其父TaskTracker詢(xún)問(wèn)此作業(yè)個(gè)Map任務(wù)的完成狀況(TaskTracker要向JobTracker詢(xún)問(wèn)后再轉(zhuǎn)告給它...)。當(dāng)獲取到相關(guān)Map任務(wù)執(zhí)行服務(wù)器的信息后,都會(huì)有一個(gè)線(xiàn)程開(kāi)啟,做具體的拷貝工作。同時(shí),還有一個(gè)內(nèi)存Merger線(xiàn)程和一個(gè)文件Merger線(xiàn)程在同步工作,它們將新鮮下載過(guò)來(lái)的文件(可能在內(nèi)存中,簡(jiǎn)單的統(tǒng)稱(chēng)為文件...),做著歸并排序,以此,節(jié)約時(shí)間,降低輸入文件的數(shù)量,為后續(xù)的排序工作減負(fù)。。。
Sort,排序工作,就相當(dāng)于上述排序工作的一個(gè)延續(xù)。它會(huì)在所有的文件都拷貝完畢后進(jìn)行,因?yàn)殡m然同步有做著歸并的工作,但可能留著尾巴,沒(méi)做徹底。經(jīng)過(guò)這一個(gè)流程,該徹底的都徹底了,一個(gè)嶄新的、合并了所有所需Map任務(wù)輸出文件的新文件,誕生了。而那些千行萬(wàn)苦從其他各個(gè)服務(wù)器網(wǎng)羅過(guò)來(lái)的Map任務(wù)輸出文件,很快的結(jié)束了它們的歷史使命,被掃地出門(mén)一掃而光,全部刪除了。。。
所謂好戲在后頭,Reduce任務(wù)的最后一個(gè)階段,正是Reduce本身。它也會(huì)準(zhǔn)備一個(gè)OutputCollector收集輸出,與MapTask不同,這個(gè)OutputCollector更為簡(jiǎn)單,僅僅是打開(kāi)一個(gè)RecordWriter,collect一次,write一次。最大的不同在于,這次傳入RecordWriter的文件系統(tǒng),基本都是分布式文件系統(tǒng),或者說(shuō)是HDFS。而在輸入方面,ReduceTask會(huì)從JobConf那里調(diào)用一堆getMapOutputKeyClass、getMapOutputValueClass、getOutputKeyComparator等等之類(lèi)的自定義類(lèi),構(gòu)造出Reducer所需的鍵類(lèi)型,和值的迭代類(lèi)型Iterator(一個(gè)鍵到了這里一般是對(duì)應(yīng)一組值)。具體實(shí)現(xiàn)頗為拐彎抹角,建議看一下Merger.MergeQueue,RawKeyValueIterator,ReduceTask.ReduceValuesIterator等等之類(lèi)的實(shí)現(xiàn)。有了輸入,有了輸出,不斷循環(huán)調(diào)用自定義的Reducer,最終,Reduce階段完成。。。
VI. 分布式支持
1、服務(wù)器正確性保證
Hadoop Map/Reduce服務(wù)器狀況和HDFS很類(lèi)似,由此可知,救死扶傷的方法也是大同小異。廢話(huà)不多說(shuō)了,直接切正題。同作為客戶(hù)端,Map/Reduce的客戶(hù)端只是將作業(yè)提交,就開(kāi)始搬個(gè)板凳看戲,沒(méi)有占茅坑的行動(dòng)。因此,一旦它掛了,也就掛了,不傷大雅。而任務(wù)服務(wù)器,也需要隨時(shí)與作業(yè)服務(wù)器保持心跳聯(lián)系,一旦有了問(wèn)題,作業(yè)服務(wù)器可以將其上運(yùn)行的任務(wù),移交給它人完成。作業(yè)服務(wù)器,作為一個(gè)單點(diǎn),非常類(lèi)似的是利用還原點(diǎn)(等同于HDFS的鏡像)和歷史記錄(等同于HDFS的日志),來(lái)進(jìn)行恢復(fù)。其上,需要持久化用于恢復(fù)的內(nèi)容,包含作業(yè)狀況、任務(wù)狀況、各個(gè)任務(wù)嘗試的工作狀況等。有了這些內(nèi)容,再加上任務(wù)服務(wù)器的動(dòng)態(tài)注冊(cè),就算挪了個(gè)窩,還是很容易恢復(fù)的。JobHistory是歷史記錄相關(guān)的一個(gè)靜態(tài)類(lèi),本來(lái),它也就是一個(gè)干寫(xiě)日志活的,只是在Hadoop的實(shí)現(xiàn)中,對(duì)日志的寫(xiě)入做了面向?qū)ο蟮姆庋b,同時(shí)又大量用到觀(guān)察者模式做了些嵌入,使得看起來(lái)不是那么直觀(guān)。本質(zhì)上,它就是打開(kāi)若干個(gè)日志文件,利用各類(lèi)接口來(lái)往里面寫(xiě)內(nèi)容。只不過(guò),這些日志,會(huì)放在分布式文件系統(tǒng)中,就不需要像HDFS那樣,來(lái)一個(gè)SecondXXX隨時(shí)候命,由此可見(jiàn),有巨人在腳下踩著,真好。JobTracker.RecoveryManager類(lèi)是作業(yè)服務(wù)器中用于進(jìn)行恢復(fù)相關(guān)的事情,當(dāng)作業(yè)服務(wù)器啟動(dòng)的時(shí)候,會(huì)調(diào)用其recover方法,恢復(fù)日志文件中的內(nèi)容。其中步驟,注釋中寫(xiě)的很清楚,請(qǐng)自行查看。。。
2、任務(wù)執(zhí)行的正確和速度
整個(gè)作業(yè)流程的執(zhí)行,秉承著木桶原理。執(zhí)行的最慢的Map任務(wù)和Reduce任務(wù),決定了系統(tǒng)整體執(zhí)行時(shí)間(當(dāng)然,如果執(zhí)行時(shí)間在整個(gè)流程中占比例很小的話(huà),也許就微不足道了...)。因此,盡量加快最慢的任務(wù)執(zhí)行速度,成為提高整體速度關(guān)鍵。所使用的策略,簡(jiǎn)約而不簡(jiǎn)單,就是一個(gè)任務(wù)多次執(zhí)行。當(dāng)所有未執(zhí)行的任務(wù)都分配出去了,并且先富起來(lái)的那部分任務(wù)已經(jīng)完成了,并還有任務(wù)服務(wù)器孜孜不倦的索取任務(wù)的時(shí)候,作業(yè)服務(wù)器會(huì)開(kāi)始炒剩飯,把那些正在吭哧吭哧在某個(gè)服務(wù)器上慢慢執(zhí)行的任務(wù),再把此任務(wù)分配到一個(gè)新的任務(wù)服務(wù)器上,同時(shí)執(zhí)行。兩個(gè)服務(wù)器各盡其力,成王敗寇,先結(jié)束者的結(jié)果將被采納。這樣的策略,隱含著一個(gè)假設(shè),就是我們相信,輸入文件的分割算法是公平的,某個(gè)任務(wù)執(zhí)行慢,并不是由于這個(gè)任務(wù)本身負(fù)擔(dān)太重,而是由于服務(wù)器不爭(zhēng)氣負(fù)擔(dān)太重能力有限或者是即將撒手西去,給它換個(gè)新環(huán)境,人挪死樹(shù)挪活事半功倍。。。
當(dāng)然,肯定有哽咽的任務(wù),不論是在哪個(gè)服務(wù)器上,都無(wú)法順利完成。這就說(shuō)明,此問(wèn)題不在于服務(wù)器上,而是任務(wù)本身天資有缺憾。缺憾在何處?每個(gè)作業(yè),功能代碼都是一樣的,別的任務(wù)成功了,就是這個(gè)任務(wù)不成功,很顯然,問(wèn)題出在輸入那里。輸入中有非法的輸入條目,導(dǎo)致程序無(wú)法辨識(shí),只能揮淚惜別。說(shuō)到這里,解決策略也浮出水面了,三十六計(jì)走位上,惹不起,還是躲得起的。在MapTask中的MapTask.SkippingRecordReader<K, V>和ReduceTask里的ReduceTask.SkippingReduceValuesIterator<KEY,VALUE>,都是用于干這個(gè)事情的。它們的原理很簡(jiǎn)單,就是在讀一條記錄前,把當(dāng)前的位置信息,封裝成SortedRanges.Range對(duì)象,經(jīng)由Task的reportNextRecordRange方法提交到TaskTracker上去。TaskTracker會(huì)把這些內(nèi)容,擱在TaskStatus對(duì)象中,隨著心跳消息,匯報(bào)到JobTracker上面。這樣,作業(yè)服務(wù)器就可以隨時(shí)隨刻了解清楚,每個(gè)任務(wù)正讀取在那個(gè)位置,一旦出錯(cuò),再次執(zhí)行的時(shí)候,就在分配的任務(wù)信息里面添加一組SortedRanges信息。MapTask或ReduceTask讀取的時(shí)候,會(huì)看一下這些區(qū)域,如果當(dāng)前區(qū)域正好處于上述雷區(qū),跳過(guò)不讀。如此反復(fù),正可謂,道路曲折,前途光明啊。。。
VII. 總結(jié)
對(duì)于Map/Reduce而言,真正的困難,在于提高其適應(yīng)能力,打造一款能夠包治百病的執(zhí)行框架。Hadoop已經(jīng)做得很好了,但只有真正搞清楚了整個(gè)流程,你才能幫助它做的更好。。。
所謂分布式,在這里,很狹義的指代以Google的三駕馬車(chē),
GFS、
Map/Reduce、
BigTable為框架核心的分布式存儲(chǔ)和計(jì)算系統(tǒng)。通常如我一樣初學(xué)的人,會(huì)以Google這幾份經(jīng)典的論文作為開(kāi)端的。它們勾勒出了分布式存儲(chǔ)和計(jì)算的一個(gè)基本藍(lán)圖,已可窺見(jiàn)其幾分風(fēng)韻,但終究還是由于缺少一些實(shí)現(xiàn)的代碼和示例,色彩有些斑駁,缺少了點(diǎn)感性。幸好我們還有Open Source,還有Hadoop。
Hadoop是一個(gè)基于Java實(shí)現(xiàn)的,開(kāi)源的,分布式存儲(chǔ)和計(jì)算的項(xiàng)目。作為這個(gè)領(lǐng)域最富盛名的開(kāi)源項(xiàng)目之一,它的使用者也是大牌如云,包括了Yahoo,Amazon,F(xiàn)acebook等等(好吧,還可能有校內(nèi),不過(guò)這真的沒(méi)啥分量...)。Hadoop本身,實(shí)現(xiàn)的是分布式的文件系統(tǒng)HDFS,和分布式的計(jì)算(Map/Reduce)框架,此外,它還不是一個(gè)人在戰(zhàn)斗,Hadoop包含一系列擴(kuò)展項(xiàng)目,包括了分布式文件數(shù)據(jù)庫(kù)
HBase(對(duì)應(yīng)Google的BigTable),分布式協(xié)同服務(wù)
ZooKeeper(對(duì)應(yīng)Google的
Chubby),等等。。。
如此,一個(gè)看上去不錯(cuò)的黃金搭檔浮出水面,Google的論文 + Hadoop的實(shí)現(xiàn),順著論文的框架看具體的實(shí)現(xiàn),用實(shí)現(xiàn)來(lái)進(jìn)一步理解論文的邏輯,看上去至少很美。網(wǎng)上有很多前輩們,做過(guò)Hadoop相關(guān)的源碼剖析工作,我關(guān)注最多的是
這里,目前博主已經(jīng)完成了HDFS的剖析工作,Map/Reduce的剖析正火熱進(jìn)行中,更新頻率之高,剖析之詳盡,都是難得一見(jiàn)的,所以,走過(guò)路過(guò)一定不要錯(cuò)過(guò)了。此外,還有很多Hadoop的關(guān)注者和使用者貼過(guò)相關(guān)的文章,比如:
這里,
這里。也可以去
Hadoop的中文站點(diǎn)(不知是民間還是官方...),搜羅一些學(xué)習(xí)資料。。。
我個(gè)人從上述資料中受益匪淺,而我自己要做的整理,與原始的源碼剖析有些不同,不是依照實(shí)現(xiàn)的模塊,而是基于論文的脈絡(luò)和實(shí)現(xiàn)這樣系統(tǒng)的基本脈絡(luò)來(lái)進(jìn)行的,也算,從另一個(gè)角度給出一些東西吧。鑒于個(gè)人對(duì)于分布式系統(tǒng)的理解非常的淺薄,缺少足夠的實(shí)踐經(jīng)驗(yàn),深入的問(wèn)題就不班門(mén)弄斧了,僅做梳理和解析,大牛至此,可繞路而行了。。。
一. 分布式文件系統(tǒng)
分布式文件系統(tǒng),在整個(gè)分布式系統(tǒng)體系中處于最低層最基礎(chǔ)的地位,存儲(chǔ)嘛,沒(méi)了數(shù)據(jù),再好的計(jì)算平臺(tái),再完善的數(shù)據(jù)庫(kù)系統(tǒng),都成了無(wú)水之舟了。那么,什么是分布式文件系統(tǒng),顧名思義,就是分布式+文件系統(tǒng)。它包含這兩個(gè)方面的內(nèi)涵,從文件系統(tǒng)的客戶(hù)使用的角度來(lái)看,它就是一個(gè)標(biāo)準(zhǔn)的文件系統(tǒng),提供了一系列API,由此進(jìn)行文件或目錄的創(chuàng)建、移動(dòng)、刪除,以及對(duì)文件的讀寫(xiě)等操作。從內(nèi)部實(shí)現(xiàn)來(lái)看,分布式的系統(tǒng)則不再和普通文件系統(tǒng)一樣負(fù)責(zé)管理本地磁盤(pán),它的文件內(nèi)容和目錄結(jié)構(gòu)都不是存儲(chǔ)在本地磁盤(pán)上,而是通過(guò)網(wǎng)絡(luò)傳輸?shù)竭h(yuǎn)端系統(tǒng)上。并且,同一個(gè)文件存儲(chǔ)不只是在一臺(tái)機(jī)器上,而是在一簇機(jī)器上分布式存儲(chǔ),協(xié)同提供服務(wù),正所謂分布式。。。
因此,考量一個(gè)分布式文件系統(tǒng)的實(shí)現(xiàn),其實(shí)不妨可以從這兩方面來(lái)分別剖析,而后合二為一。首先,看它如何去實(shí)現(xiàn)文件系統(tǒng)所需的基本增刪改查的功能。然后,看它如何考慮分布式系統(tǒng)的特點(diǎn),提供更好的容錯(cuò)性,負(fù)載平衡,等等之類(lèi)的。這二者合二為一,就明白了一個(gè)分布式文件系統(tǒng),整體的實(shí)現(xiàn)模式。。。
I. 術(shù)語(yǔ)對(duì)照
說(shuō)任何東西,都需要統(tǒng)一一下語(yǔ)言先,不然明明說(shuō)的一個(gè)意思,卻容易被理解到另一個(gè)地方去。Hadoop的分布式文件系統(tǒng)HDFS,基本是按照Google論文中的GFS的架構(gòu)來(lái)實(shí)現(xiàn)的。但是,HDFS為了彰顯其不走尋常路的本性,其中的大量術(shù)語(yǔ),都與GFS截然不同。明明都是一個(gè)枝上長(zhǎng)的土豆,它偏偏就要叫山藥蛋,弄得水火不容的,苦了我們看客。秉承老好人,誰(shuí)也不得罪的方針,文中,既不采用GFS的叫法,也不采用Hadoop的稱(chēng)謂,而是另辟蹊徑,自立門(mén)戶(hù),搞一套自己的中文翻譯,為了避免不必要的痛楚,特此先來(lái)一帖術(shù)語(yǔ)對(duì)照表,要不懂查一查,包治百病。。。
| 文中所用翻譯 |
HDFS中的術(shù)語(yǔ) |
GFS中的術(shù)語(yǔ) |
術(shù)語(yǔ)解釋 |
| 主控服務(wù)器 |
NameNode |
Master |
整個(gè)文件系統(tǒng)的大腦,它提供整個(gè)文件系統(tǒng)的目錄信息,并且管理各個(gè)數(shù)據(jù)服務(wù)器。 |
| 數(shù)據(jù)服務(wù)器 |
DataNode |
Chunk Server |
分布式文件系統(tǒng)中的每一個(gè)文件,都被切分成若干個(gè)數(shù)據(jù)塊,每一個(gè)數(shù)據(jù)塊都被存儲(chǔ)在不同的服務(wù)器上,此服務(wù)器稱(chēng)之為數(shù)據(jù)服務(wù)器。 |
| 數(shù)據(jù)塊 |
Block |
Chunk |
每個(gè)文件都會(huì)被切分成若干個(gè)塊,每一塊都有連續(xù)的一段文件內(nèi)容,是存儲(chǔ)的基恩單位,在這里統(tǒng)一稱(chēng)做數(shù)據(jù)塊。 |
| 數(shù)據(jù)包 |
Packet |
無(wú) |
客戶(hù)端寫(xiě)文件的時(shí)候,不是一個(gè)字節(jié)一個(gè)字節(jié)寫(xiě)入文件系統(tǒng)的,而是累計(jì)到一定數(shù)量后,往文件系統(tǒng)中寫(xiě)入一次,每發(fā)送一次的數(shù)據(jù),都稱(chēng)為一個(gè)數(shù)據(jù)包。 |
| 傳輸塊 |
Chunk |
無(wú) |
在每一個(gè)數(shù)據(jù)包中,都會(huì)將數(shù)據(jù)切成更小的塊,每一個(gè)塊配上一個(gè)奇偶校驗(yàn)碼,這樣的塊,就是傳輸塊。 |
| 備份主控服務(wù)器 |
SecondaryNameNode |
無(wú) |
備用的主控服務(wù)器,在身后默默的拉取著主控服務(wù)器 的日志,等待主控服務(wù)器犧牲后被扶正。 |
*注:本文采用的Hadoop是0.19.0版本。
II. 基本架構(gòu)
1. 服務(wù)器介紹
與單機(jī)的文件系統(tǒng)不同,分布式文件系統(tǒng)不是將這些數(shù)據(jù)放在一塊磁盤(pán)上,由上層操作系統(tǒng)來(lái)管理。而是存放在一個(gè)服務(wù)器集群上,由集群中的服務(wù)器,各盡其責(zé),通力合作,提供整個(gè)文件系統(tǒng)的服務(wù)。其中重要的服務(wù)器包括:主控服務(wù)器(Master/NameNode),數(shù)據(jù)服務(wù)器(ChunkServer/DataNode),和客戶(hù)服務(wù)器。HDFS和GFS都是按照這個(gè)架構(gòu)模式搭建的。個(gè)人覺(jué)得,其中設(shè)計(jì)的最核心內(nèi)容是:文件的目錄結(jié)構(gòu)獨(dú)立存儲(chǔ)在一個(gè)主控服務(wù)器上,而具體文件數(shù)據(jù),拆分成若干塊,冗余的存放在不同的數(shù)據(jù)服務(wù)器上。
存儲(chǔ)目錄結(jié)構(gòu)的主控服務(wù)器,在GFS中稱(chēng)為Master,在HDFS中稱(chēng)為NameNode。這兩個(gè)名字,叫得都有各自的理由,是瞎子摸象各表一面。Master是之于數(shù)據(jù)服務(wù)器來(lái)叫的,它做為數(shù)據(jù)服務(wù)器的領(lǐng)導(dǎo)同志存在,管理各個(gè)數(shù)據(jù)服務(wù)器,收集它們的信息,了解所有數(shù)據(jù)服務(wù)器的生存現(xiàn)狀,然后給它們分配任務(wù),指揮它們齊心協(xié)力為系統(tǒng)服務(wù);而NameNode是針對(duì)客戶(hù)端來(lái)叫的,對(duì)于客戶(hù)端而言,主控服務(wù)器上放著所有的文件目錄信息,要找一個(gè)文件,必須問(wèn)問(wèn)它,由此而的此名。。。
主控服務(wù)器在整個(gè)集群中,同時(shí)提供服務(wù)的只存在一個(gè),如果它不幸犧牲的話(huà),會(huì)有后備軍立刻前赴后繼的跟上,但,同一時(shí)刻,需要保持一山不容二虎的態(tài)勢(shì)。這種設(shè)計(jì)策略,避免了多臺(tái)服務(wù)器間即時(shí)同步數(shù)據(jù)的代價(jià),而同時(shí),它也使得主控服務(wù)器很可能成為整個(gè)架構(gòu)的瓶頸所在。因此,盡量為主控服務(wù)器減負(fù),不然它做太多的事情,就自然而然的晉升成了一個(gè)分布式文件系統(tǒng)的設(shè)計(jì)要求。。。
每一個(gè)文件的具體數(shù)據(jù),被切分成若干個(gè)數(shù)據(jù)塊,冗余的存放在數(shù)據(jù)服務(wù)器。通常的配置,每一個(gè)數(shù)據(jù)塊的大小為64M,在三個(gè)數(shù)據(jù)服務(wù)器上冗余存放(這個(gè)64M,不是隨便得來(lái)的,而是經(jīng)過(guò)反復(fù)實(shí)踐得到的。因?yàn)槿绻螅菀自斐蔁狳c(diǎn)的堆疊,大量的操作集中在一臺(tái)數(shù)據(jù)服務(wù)器上,而如果太小的話(huà),附加的控制信息傳輸成本,又太高了。因此沒(méi)有比較特定的業(yè)務(wù)需求,可以考慮維持此配置...)。數(shù)據(jù)服務(wù)器是典型的四肢發(fā)達(dá)頭腦簡(jiǎn)單的苦力,其主要的工作模式就是定期向主控服務(wù)器匯報(bào)其狀況,然后等待并處理命令,更快更安全的存放好數(shù)據(jù)。。。
此外,整個(gè)分布式文件系統(tǒng)還有一個(gè)重要角色是客戶(hù)端。它不和主控服務(wù)和數(shù)據(jù)服務(wù)一樣,在一個(gè)獨(dú)立的進(jìn)程中提供服務(wù),它只是以一個(gè)類(lèi)庫(kù)(包)的模式存在,為用戶(hù)提供了文件讀寫(xiě)、目錄操作等APIs。當(dāng)用戶(hù)需要使用分布式文件系統(tǒng)進(jìn)行文件讀寫(xiě)的時(shí)候,把客戶(hù)端相關(guān)包給配置上,就可以通過(guò)它來(lái)享受分布式文件系統(tǒng)提供的服務(wù)了。。。
2. 數(shù)據(jù)分布
一個(gè)文件系統(tǒng)中,最重要的數(shù)據(jù),其實(shí)就是整個(gè)文件系統(tǒng)的目錄結(jié)構(gòu)和具體每個(gè)文件的數(shù)據(jù)。具體的文件數(shù)據(jù)被切分成數(shù)據(jù)塊,存放在數(shù)據(jù)服務(wù)器上。每一個(gè)文件數(shù)據(jù)塊,在數(shù)據(jù)服務(wù)器上都表征為出雙入隊(duì)的一對(duì)文件(這是普通的Linux文件),一個(gè)是數(shù)據(jù)文件,一個(gè)是附加信息的元文件,在這里,不妨把這對(duì)文件簡(jiǎn)稱(chēng)為
數(shù)據(jù)塊文件。數(shù)據(jù)塊文件存放在
數(shù)據(jù)目錄下,它有一個(gè)名為current的根目錄,然后里面有若干個(gè)數(shù)據(jù)塊文件和從dir0-dir63的最多64個(gè)的子目錄,子目錄內(nèi)部結(jié)構(gòu)等同于current目錄,依次類(lèi)推(更詳細(xì)的描述,參見(jiàn)
這里)。個(gè)人覺(jué)得,這樣的架構(gòu),有利于控制同一目錄下文件的數(shù)量,加快檢索速度。。。
這是磁盤(pán)上的物理結(jié)構(gòu),與之對(duì)應(yīng)的,是內(nèi)存中的數(shù)據(jù)結(jié)構(gòu),用以表征這樣的磁盤(pán)結(jié)構(gòu),方便讀寫(xiě)操作的進(jìn)行。
Block類(lèi)用于表示數(shù)據(jù)塊,而FSDataset類(lèi)是數(shù)據(jù)服務(wù)器管理文件塊的數(shù)據(jù)結(jié)構(gòu),其中,FSDataset.FSDir對(duì)應(yīng)著數(shù)據(jù)塊文件和目錄,FSDataset.FSVolume對(duì)應(yīng)著一個(gè)數(shù)據(jù)目錄,FSDataset.FSVolumeSet是FSVolume的集合,每一個(gè)FSDataset有一個(gè)FSVolumeSet。多個(gè)數(shù)據(jù)目錄,可以放在不同的磁盤(pán)上,這樣有利于加快磁盤(pán)操作的速度。相關(guān)的類(lèi)圖,可以參看這里 。。。
此外,與FSVolume對(duì)應(yīng)的,還有一個(gè)數(shù)據(jù)結(jié)構(gòu),就是
DataStorage,它是
Storage的子類(lèi),提供了升級(jí)、回滾等支持。但與FSVolume不一樣,它不需要了解數(shù)據(jù)塊文件的具體內(nèi)容,它只知道有這么一堆文件放這里,會(huì)有不同版本的升級(jí)需求,它會(huì)處理怎么把它們升級(jí)回滾之類(lèi)的業(yè)務(wù)(關(guān)于Storage,可以參見(jiàn)
這里)。而FSVolume提供的接口,都基本上是和Block相關(guān)的。。。
相比數(shù)據(jù)服務(wù)器,主控服務(wù)器的數(shù)據(jù)量不大,但邏輯更為復(fù)雜。主控服務(wù)器主要有三類(lèi)數(shù)據(jù):文件系統(tǒng)的目錄結(jié)構(gòu)數(shù)據(jù),各個(gè)文件的分塊信息,數(shù)據(jù)塊的位置信息(就數(shù)據(jù)塊放置在哪些數(shù)據(jù)服務(wù)器上...)。在GFS和HDFS的架構(gòu)中,只有文件的目錄結(jié)構(gòu)和分塊信息才會(huì)被持久化到本地磁盤(pán)上,而數(shù)據(jù)塊的位置信息則是通過(guò)動(dòng)態(tài)匯總過(guò)來(lái)的,僅僅存活在內(nèi)存數(shù)據(jù)結(jié)構(gòu)中,機(jī)器掛了,就灰飛煙滅了。每一個(gè)數(shù)據(jù)服務(wù)器啟動(dòng)后,都會(huì)向主控服務(wù)器發(fā)送注冊(cè)消息,將其上數(shù)據(jù)塊的狀況都告知于主控服務(wù)器。俗話(huà)說(shuō),簡(jiǎn)單就是美,根據(jù)DRY原則,保存的冗余信息越少,出現(xiàn)不一致的可能性越低,付出一點(diǎn)點(diǎn)時(shí)間的代價(jià),換取了一大把邏輯上的簡(jiǎn)單性,絕對(duì)應(yīng)該是一個(gè)包賺不賠的買(mǎi)賣(mài)。。。
在HDFS中,FSNamespacesystem類(lèi)就負(fù)責(zé)保管文件系統(tǒng)的目錄結(jié)構(gòu)以及每個(gè)文件的分塊狀況的,其中,前者是由FSDirectory類(lèi)來(lái)負(fù)責(zé),后者是各個(gè)INodeFile本身維護(hù)。在INodeFile里面,有一個(gè)BlockInfo的數(shù)組,保存著與該文件相關(guān)的所有數(shù)據(jù)塊信息,BlockInfo中包含了從數(shù)據(jù)塊到數(shù)據(jù)服務(wù)器的映射,INodeFile只需要知道一個(gè)偏移量,就可以提供相關(guān)的數(shù)據(jù)塊,和數(shù)據(jù)塊存放的數(shù)據(jù)服務(wù)器信息。。。
3、服務(wù)器間協(xié)議
在Hadoop的實(shí)現(xiàn)中,部署了一套R(shí)PC機(jī)制,以此來(lái)實(shí)現(xiàn)各服務(wù)間的通信協(xié)議。在Hadoop中,每一對(duì)服務(wù)器間的通信協(xié)議,都定義成為一個(gè)接口。服務(wù)端的類(lèi)實(shí)現(xiàn)該接口,并且建立RPC服務(wù),監(jiān)聽(tīng)相關(guān)的接口,在獨(dú)立的線(xiàn)程處理RPC請(qǐng)求。客戶(hù)端則可以實(shí)例化一個(gè)該接口的代理對(duì)象,調(diào)用該接口的相應(yīng)方法,執(zhí)行一次同步的通信,傳入相應(yīng)參數(shù),接收相應(yīng)的返回值。基于此RPC的通信模式,是一個(gè)消息拉取的流程,RPC服務(wù)器等待RPC客戶(hù)端的調(diào)用,而不會(huì)先發(fā)制人主動(dòng)把相關(guān)信息推送到RPC客戶(hù)端去。。。
其實(shí)RPC的模式和原理,實(shí)在是沒(méi)啥好說(shuō)的,之所以說(shuō),是因?yàn)榭梢酝ㄟ^(guò)把握好這個(gè),徹底理順Hadoop各服務(wù)器間的通信模式。Hadoop會(huì)定義一些列的RPC接口,只需要看誰(shuí)實(shí)現(xiàn),誰(shuí)調(diào)用,就可以知道誰(shuí)和誰(shuí)通信,都做些啥事情,圖中服務(wù)器的基本架構(gòu)、各服務(wù)所使用的協(xié)議、調(diào)用方向、以及協(xié)議中的基本內(nèi)容。。。
III. 基本的文件操作
基本的文件操作,可以分成兩類(lèi),一個(gè)是對(duì)文件目錄結(jié)構(gòu)的操作,比如文件和目錄的創(chuàng)建、刪除、移動(dòng)、更名等等;另一個(gè)是對(duì)文件數(shù)據(jù)流的操作,包括讀取和寫(xiě)入文件數(shù)據(jù)。當(dāng)然,文件讀和寫(xiě),是有本質(zhì)區(qū)別的,尤其是在數(shù)據(jù)冗余的情況下,因此,當(dāng)成兩類(lèi)操作也不足為過(guò)。此外,要具體到讀寫(xiě)的類(lèi)別,也是可以再繼續(xù)分類(lèi)下去的。在GFS的論文中,對(duì)于分布式文件系統(tǒng)的讀寫(xiě)場(chǎng)景有一個(gè)重要的假定(其實(shí)是從實(shí)際業(yè)務(wù)角度得來(lái)的...):就是文件的讀取是由大數(shù)據(jù)量的連續(xù)讀取和小數(shù)據(jù)量的隨機(jī)讀取組成,文件的寫(xiě)入則基本上都是批量的追加寫(xiě),和偶爾的插入寫(xiě)(GFS中還有大量的假設(shè),它們構(gòu)成了分布式文件系統(tǒng)架構(gòu)設(shè)計(jì)的基石。每一個(gè)系統(tǒng)架構(gòu)都是搭建在一定假設(shè)上的,這些假設(shè)有些來(lái)自于實(shí)際業(yè)務(wù)的狀況,有些是因?yàn)樘焐臈l件約束,不基于假設(shè)理解設(shè)計(jì),肯定會(huì)有失偏頗...)。在GFS中,對(duì)文件的寫(xiě)入分成追加寫(xiě)和插入寫(xiě)都有所支持,但是,在HDFS中僅僅支持追加寫(xiě),這大大降低了復(fù)雜性。關(guān)于HDFS與GFS的一些不同,可以參看
這里。。。
1. 文件和目錄的操作
文件目錄的信息,全部囤積在主控服務(wù)器上,因此,所有對(duì)文件目錄的操作,只會(huì)直接涉及到客戶(hù)端和主控服務(wù)器。整個(gè)目錄相關(guān)的操作流程基本都是這樣的:客戶(hù)端DFSClient調(diào)用ClientProtocol定義的相關(guān)函數(shù),該操作通過(guò)RPC傳送到其實(shí)現(xiàn)者主控服務(wù)器NameNode那里,NameNode做相關(guān)的處理后(很少...),調(diào)用FSNamesystem的相關(guān)函數(shù)。在FSNamesystem中,往往是做一些驗(yàn)證和租約操作,具體的目錄結(jié)構(gòu)操作交由FSDirectory的相應(yīng)函數(shù)來(lái)操作。最后,依次返回,經(jīng)由RPC傳送回客戶(hù)端。具體各操作涉及到的函數(shù)和具體步驟,參見(jiàn)下表:
| 相關(guān)操作 |
ClientProtocol / NameNode |
FSNamesystem |
FSDirectory |
關(guān)鍵步驟 |
| 創(chuàng)建文件 |
create |
startFile |
addFile |
1. 檢查是否有寫(xiě)權(quán)限; 2. 檢查是否已經(jīng)存在此文件,如果是覆寫(xiě),則先進(jìn)行刪除操作; 3. 在指定路徑下添加INodeFileUnderConstruction的文件實(shí)例; 4. 寫(xiě)日志; 5. 簽訂租約。 |
| 創(chuàng)建目錄 |
mkdirs |
mkdirs |
mkdirs |
1. 檢查指定目錄是否是目錄; 2. 檢查是否有相關(guān)權(quán)限; 3. 在指定路徑的INode下,添加子節(jié)點(diǎn); 4. 寫(xiě)日志。 |
| 改名操作 |
rename |
renameTo |
renameTo |
1. 檢查相關(guān)路徑的權(quán)限; 2. 從老路徑下移除,在新路徑下添加; 3. 修改相關(guān)父路徑的修改時(shí)間; 4. 寫(xiě)日志; 5. 將租約從老路徑移動(dòng)到新路徑下。 |
| 刪除操作 |
delete |
delete |
delete |
1. 如果不是遞歸刪除,確認(rèn)指定路徑是否是空目錄; 2. 檢查相關(guān)權(quán)限; 3. 在目錄結(jié)構(gòu)上移除相關(guān)INode; 4. 修改父路徑的修改時(shí)間; 5. 將相關(guān)的數(shù)據(jù)塊,放入到廢棄隊(duì)列中去,等待處理; 6. 寫(xiě)日志; 7. 廢棄相關(guān)路徑的租約。 |
| 設(shè)置權(quán)限 |
setPermission |
setPermission |
setPermission |
1. 檢查owner判斷是否有操作權(quán)限; 2. 修改指定路徑下INode的權(quán)限; 3. 寫(xiě)日志。 |
| 設(shè)置用戶(hù) |
setOwner |
setOwner |
setOwner |
1. 檢查是否有操作權(quán)限; 2. 修改指定路徑下INode的權(quán)限; 3. 寫(xiě)日志。 |
| 設(shè)置時(shí)間 |
setTimes |
setTimes |
setTimes |
1. 檢查是否有寫(xiě)權(quán)限; 2. 修改指定路徑INode的時(shí)間信息; 3. 寫(xiě)日志。 |
從上表可以看到,其實(shí)有的操作本質(zhì)上還是涉及到了數(shù)據(jù)服務(wù)器,比如文件創(chuàng)建和刪除操作。但是,之前提到,主控服務(wù)器只于數(shù)據(jù)服務(wù)器是一個(gè)等待拉取的地位,它們不會(huì)主動(dòng)聯(lián)系數(shù)據(jù)服務(wù)器,將指令傳輸給它們,而是放到相應(yīng)的數(shù)據(jù)結(jié)構(gòu)中,等待數(shù)據(jù)服務(wù)器來(lái)取。這樣的設(shè)計(jì),可以減少通信的次數(shù),加快操作的執(zhí)行速度。。。
另,上述步驟中,有些日志和租約相關(guān)的操作,從概念上來(lái)說(shuō),和目錄操作其實(shí)沒(méi)有任何聯(lián)系,但是,為了滿(mǎn)足分布式系統(tǒng)的需求,這些操作是非常有必要的,在此,按下不表。。。
2、文件的讀取
不論是文件讀取,還是文件的寫(xiě)入,主控服務(wù)器扮演的都是中介的角色。客戶(hù)端把自己的需求提交給主控服務(wù)器,主控服務(wù)器挑選合適的數(shù)據(jù)服務(wù)器,介紹給客戶(hù)端,讓客戶(hù)端和數(shù)據(jù)服務(wù)器單聊,要讀要寫(xiě)隨你們便。這種策略類(lèi)似于DMA,降低了主控服務(wù)器的負(fù)載,提高了效率。。。
因此,在文件讀寫(xiě)操作中,最主要的通信,發(fā)生在客戶(hù)端與數(shù)據(jù)服務(wù)器之間。它們之間跑的協(xié)議是
ClientDatanodeProtocol。從這個(gè)協(xié)議中間,你無(wú)法看到和讀寫(xiě)相關(guān)的接口,因?yàn)椋贖adoop中,
讀寫(xiě)操作是不走RPC機(jī)制的,而是另立門(mén)戶(hù),獨(dú)立搭了一套通信框架。在數(shù)據(jù)服務(wù)器一端,
DataNode類(lèi)中有一個(gè)
DataXceiverServer類(lèi)的實(shí)例,它在一個(gè)單獨(dú)的線(xiàn)程等待請(qǐng)求,一旦接到,就啟動(dòng)一個(gè)
DataXceiver的線(xiàn)程,處理此次請(qǐng)求。一個(gè)請(qǐng)求一個(gè)線(xiàn)程,對(duì)于數(shù)據(jù)服務(wù)器來(lái)說(shuō),邏輯上很簡(jiǎn)單。當(dāng)下,DataXceiver支持的請(qǐng)求類(lèi)型有六種,具體的請(qǐng)求包和回復(fù)包格式,請(qǐng)參見(jiàn)
這里,
這里,
這里。在Hadoop的實(shí)現(xiàn)中,并沒(méi)有用類(lèi)來(lái)封裝這些請(qǐng)求,而是按流的次序?qū)懴聛?lái),這給代碼閱讀帶來(lái)挺多的麻煩,也對(duì)代碼的維護(hù)帶來(lái)一定的困難,不知道是出于何種考慮。。。
相比于寫(xiě),文件的讀取實(shí)在是一個(gè)簡(jiǎn)單的過(guò)程。在客戶(hù)端DFSClient中,有一個(gè)DFSClient.DFSInputStream類(lèi)。當(dāng)需要讀取一個(gè)文件的時(shí)候,會(huì)生成一個(gè)DFSInputStream的實(shí)例。它會(huì)先調(diào)用ClientProtocol定義getBlockLocations接口,提供給NameNode文件路徑、讀取位置、讀取長(zhǎng)度信息,從中取得一個(gè)LocatedBlocks類(lèi)的對(duì)象,這個(gè)對(duì)象包含一組LocatedBlock,那里面有所規(guī)定位置中包含的所有數(shù)據(jù)塊信息,以及數(shù)據(jù)塊對(duì)應(yīng)的所有數(shù)據(jù)服務(wù)器的位置信息。當(dāng)讀取開(kāi)始后,DFSInputStream會(huì)先嘗試從某個(gè)數(shù)據(jù)塊對(duì)應(yīng)的一組數(shù)據(jù)服務(wù)器中選出一個(gè),進(jìn)行連接。這個(gè)選取算法,在當(dāng)下的實(shí)現(xiàn)中,非常簡(jiǎn)單,就是選出第一個(gè)未掛的數(shù)據(jù)服務(wù)器,并沒(méi)有加入客戶(hù)端與數(shù)據(jù)服務(wù)器相對(duì)位置的考量。讀取的請(qǐng)求,發(fā)送到數(shù)據(jù)服務(wù)器后,自然會(huì)有DataXceiver來(lái)處理,數(shù)據(jù)被一個(gè)包一個(gè)包發(fā)送回客戶(hù)端,等到整個(gè)數(shù)據(jù)塊的數(shù)據(jù)都被讀取完了,就會(huì)斷開(kāi)此鏈接,嘗試連接下一個(gè)數(shù)據(jù)塊對(duì)應(yīng)的數(shù)據(jù)服務(wù)器,整個(gè)流程,依次如此反復(fù),直到所有想讀的都讀取完了為止。。。
3、文件的寫(xiě)入
文件讀取是一個(gè)一對(duì)一的過(guò)程,一個(gè)客戶(hù)端,只需要與一個(gè)數(shù)據(jù)服務(wù)器聯(lián)系,就可以獲得所需的內(nèi)容。但是,寫(xiě)入操作,則是一個(gè)一對(duì)多的流程。一次寫(xiě)入,需要在所有存放相關(guān)數(shù)據(jù)塊的數(shù)據(jù)服務(wù)器都保持同步的更新,有任何的差池,整個(gè)流程就告失敗。。。
在分布式系統(tǒng)中,一旦涉及到寫(xiě)入操作,并發(fā)處理難免都會(huì)淪落成為一個(gè)變了相的串行操作。因?yàn)椋绻煌目蛻?hù)端如果是任意時(shí)序并發(fā)寫(xiě)入的話(huà),整個(gè)寫(xiě)入的次序無(wú)法保證,可能你寫(xiě)半條記錄我寫(xiě)半條記錄,最后出來(lái)的結(jié)果亂七八糟不可估量。在HDFS中,并發(fā)寫(xiě)入的次序控制,是由主控服務(wù)器來(lái)把握的。當(dāng)創(chuàng)建、續(xù)寫(xiě)一個(gè)文件的時(shí)候,該文件的節(jié)點(diǎn)類(lèi),由INodeFile升級(jí)成為INodeFileUnderConstruction,INodeFileUnderConstruction是INodeFile的子類(lèi),它起到一個(gè)鎖的作用。如果當(dāng)一個(gè)客戶(hù)端想創(chuàng)建或續(xù)寫(xiě)的文件是INodeFileUnderConstruction,會(huì)引發(fā)異常,因?yàn)檫@說(shuō)明這個(gè)此處有爺,請(qǐng)另尋高就,從而保持了并發(fā)寫(xiě)入的次序性。同時(shí),INodeFileUnderConstruction有包含了此時(shí)正在操作它的客戶(hù)端的信息以及最后一個(gè)數(shù)據(jù)塊的數(shù)據(jù)服務(wù)器信息,當(dāng)追加寫(xiě)的時(shí)候可以更快速的響應(yīng)。。。
與讀取類(lèi)似,DFSClient也有一個(gè)DFSClient.DFSOutputStream類(lèi),寫(xiě)入開(kāi)始,會(huì)創(chuàng)建此類(lèi)的實(shí)例。DFSOutputStream會(huì)從NameNode上拿一個(gè)LocatedBlock,這里面有最后一個(gè)數(shù)據(jù)塊的所有數(shù)據(jù)服務(wù)器的信息。這些數(shù)據(jù)服務(wù)器每一個(gè)都需要能夠正常工作(對(duì)于讀取,只要還有一個(gè)能工作的就可以實(shí)現(xiàn)...),它們會(huì)依照客戶(hù)端的位置被排列成一個(gè)有著最近物理距離和最小的序列(物理距離,是根據(jù)機(jī)器的位置定下來(lái)的...),這個(gè)排序問(wèn)題類(lèi)似于著名旅行商問(wèn)題,屬于NP復(fù)雜度,但是由于服務(wù)器數(shù)量不多,所以用最粗暴的算法,也并不會(huì)看上去不美。。。
文件寫(xiě)入,就是在這一組數(shù)據(jù)服務(wù)器上構(gòu)造成數(shù)據(jù)流的雙向流水線(xiàn)。DFSOutputStream,會(huì)與序列的第一個(gè)數(shù)據(jù)服務(wù)器建立Socket連接,發(fā)送請(qǐng)求頭,然后等待回應(yīng)。DataNode同樣是建立DataXceiver來(lái)處理寫(xiě)消息,DataXceiver會(huì)依照包中傳過(guò)來(lái)的其他服務(wù)器的信息,建立與下一個(gè)服務(wù)器的連接,并生成類(lèi)似的頭,發(fā)送給它,并等待回包。此流程依次延續(xù),直到最后一級(jí),它發(fā)送回包,反向著逐級(jí)傳遞,再次回到客戶(hù)端。如果一切順利,那么此時(shí),流水線(xiàn)建立成功,開(kāi)始正式發(fā)送數(shù)據(jù)。數(shù)據(jù)是分成一個(gè)個(gè)數(shù)據(jù)包發(fā)送的,所有寫(xiě)入的內(nèi)容,被緩存在客戶(hù)端,當(dāng)寫(xiě)滿(mǎn)64K,會(huì)被封裝成DFSOutputStream.Packet類(lèi)實(shí)例,放入DFSOutputStream的dataQueue隊(duì)列。DFSOutputStream.DataStreamer會(huì)時(shí)刻監(jiān)聽(tīng)這個(gè)隊(duì)列,一旦不為空,則開(kāi)始發(fā)送,將位于dataQueue隊(duì)首的包移動(dòng)到ackQueue隊(duì)列的隊(duì)尾,表示已發(fā)送但尚未接受回復(fù)的包隊(duì)列。同時(shí)啟動(dòng)ResponseProcessor線(xiàn)程監(jiān)聽(tīng)回包,直到收到相應(yīng)回包,才將發(fā)送包從ackQueue中移除,表示成功。每一個(gè)數(shù)據(jù)服務(wù)器的DataXceiver收到了數(shù)據(jù)包,一邊寫(xiě)入到本地文件中去,一邊轉(zhuǎn)發(fā)給下一級(jí)的數(shù)據(jù)服務(wù)器,等待回包,同前面建立流水線(xiàn)的流程。。。
當(dāng)一個(gè)數(shù)據(jù)塊寫(xiě)滿(mǎn)了之后,客戶(hù)端需要向主控服務(wù)器申請(qǐng)追加新的數(shù)據(jù)塊。這個(gè)會(huì)引起一次數(shù)據(jù)塊的分配,成功后,會(huì)將新的數(shù)據(jù)服務(wù)器組返還給客戶(hù)端。然后重新回到上述流程,繼續(xù)前行。。。
關(guān)于寫(xiě)入的流程,還可以參見(jiàn)
這里。此外,寫(xiě)入涉及到租約問(wèn)題,后續(xù)會(huì)仔細(xì)的來(lái)說(shuō)。。。
IV. 分布式支持
如果單機(jī)的文件系統(tǒng)是田里勤懇的放牛娃,那么分布式文件系統(tǒng)就是刀尖上討飯吃的馬賊了。在分布式環(huán)境中,有太多的意外,數(shù)據(jù)隨時(shí)傳輸錯(cuò)誤,服務(wù)器時(shí)刻準(zhǔn)備犧牲,很多平常稱(chēng)為異常的現(xiàn)象,在這里都需要按照平常事來(lái)對(duì)待。因此,對(duì)于分布式文件系統(tǒng)而言,僅僅是滿(mǎn)足了正常狀況下文件系統(tǒng)各項(xiàng)服務(wù)還不夠,還需要保證分布式各種意外場(chǎng)景下健康持續(xù)的服務(wù),否則,將一無(wú)是處。。。
1、服務(wù)器的錯(cuò)誤恢復(fù)
在分布式環(huán)境中,哪臺(tái)服務(wù)器犧牲都是常見(jiàn)的事情,犧牲不可怕,可怕的是你都沒(méi)有時(shí)刻準(zhǔn)備好它們會(huì)犧牲。作為一個(gè)合格的分布式系統(tǒng),HDFS當(dāng)然時(shí)刻準(zhǔn)備好了前赴后繼奮勇向前。HDFS有三類(lèi)服務(wù)器,每一類(lèi)服務(wù)器出錯(cuò)了,都有相應(yīng)的應(yīng)急策略。。。
a. 客戶(hù)端
生命最輕如鴻毛的童鞋,應(yīng)該就是客戶(hù)端了。畢竟,做為一個(gè)文件系統(tǒng)的使用者,在整個(gè)文件系統(tǒng)中的地位,難免有些歸于三流。而作為客戶(hù)端,大部分時(shí)候,犧牲了就犧牲了,沒(méi)人哀悼,無(wú)人同情,只有在在辛勤寫(xiě)入的時(shí)候,不幸辭世(機(jī)器掛了,或者網(wǎng)絡(luò)斷了,諸如此類(lèi)...),才會(huì)引起些恐慌。因?yàn)椋藭r(shí)此刻,在主控服務(wù)器上對(duì)應(yīng)的文件,正作為INodeFileUnderConstruction活著,僅僅為占有它的那個(gè)客戶(hù)端服務(wù)者,做為一個(gè)專(zhuān)一的文件,它不允許別的客戶(hù)端染指。這樣的話(huà),一旦占有它的客戶(hù)端服務(wù)者犧牲了,此客戶(hù)端會(huì)依然占著茅坑不拉屎,讓如花似玉INodeFileUnderConstruction孤孤單單守寡終身。這種事情當(dāng)然無(wú)法容忍,因此,必須有辦法解決這個(gè)問(wèn)題,辦法就是:租約。。。
租約,顧名思義,就是當(dāng)客戶(hù)端需要占用某文件的時(shí)候,與主控服務(wù)器簽訂的一個(gè)短期合同。這個(gè)合同有一個(gè)期限,在這個(gè)期限內(nèi),客戶(hù)端可以延長(zhǎng)合同期限,一旦超過(guò)期限,主控服務(wù)器會(huì)強(qiáng)行終止此租約,將這個(gè)文件的享用權(quán),分配給他人。。。
在打開(kāi)或創(chuàng)建一個(gè)文件,準(zhǔn)備追加寫(xiě)之前,會(huì)調(diào)用LeaseManager的addLease方法,在指定的路徑下與此客戶(hù)端簽訂一份租約。客戶(hù)端會(huì)啟動(dòng)DFSClient.LeaseChecker線(xiàn)程,定時(shí)輪詢(xún)調(diào)用ClientProtocol的renewLease方法,續(xù)簽租約。在主控服務(wù)器一端,有一個(gè)LeaseManager.Monitor線(xiàn)程,始終在輪詢(xún)檢查所有租約,查看是否有到期未續(xù)的租約。如果一切正常,該客戶(hù)端完成寫(xiě)操作,會(huì)關(guān)閉文件,停止租約,一旦有所意外,比如文件被刪除了,客戶(hù)端犧牲了,主控服務(wù)器都會(huì)剝奪此租約,如此,來(lái)避免由于客戶(hù)端停機(jī)帶來(lái)的資源被長(zhǎng)期霸占的問(wèn)題。。。
b. 數(shù)據(jù)服務(wù)器
當(dāng)然,會(huì)掛的不只是客戶(hù)端,海量的數(shù)據(jù)服務(wù)器是一個(gè)更不穩(wěn)定的因素。一旦某數(shù)據(jù)服務(wù)器犧牲了,并且主控服務(wù)器被蒙在鼓中,主控服務(wù)器就會(huì)變相的欺騙客戶(hù)端,給它們無(wú)法連接的讀寫(xiě)服務(wù)器列表,導(dǎo)致它們處處碰壁無(wú)法工作。因此,為了整個(gè)系統(tǒng)的穩(wěn)定,數(shù)據(jù)服務(wù)器必須時(shí)刻向主控服務(wù)器匯報(bào),保持主控服務(wù)器對(duì)其的完全了解,這個(gè)機(jī)制,就是心跳消息。在HDFS中,主控服務(wù)器NameNode實(shí)現(xiàn)了DatanodeProtocol接口,數(shù)據(jù)服務(wù)器DataNode會(huì)在主循環(huán)中,不停的調(diào)用該協(xié)議中的sendHeartbeat方法,向NameNode匯報(bào)狀況。在此調(diào)用中,DataNode會(huì)將其整體運(yùn)行狀況告知NameNode,比如:有多少可用空間、用了多大的空間,等等之類(lèi)。NameNode會(huì)記住此DataNode的運(yùn)行狀況,作為新的數(shù)據(jù)塊分配或是負(fù)載均衡的依據(jù)。當(dāng)NameNode處理完成此消息后,會(huì)將相關(guān)的指令封裝成一個(gè)DatanodeCommand對(duì)象,交還給DataNode,告訴數(shù)據(jù)服務(wù)器什么數(shù)據(jù)塊要?jiǎng)h除什么數(shù)據(jù)塊要新增等等之類(lèi),數(shù)據(jù)服務(wù)器以此為自己的行動(dòng)依據(jù)。。。
但是,sendHeartbeat并沒(méi)有提供本地的數(shù)據(jù)塊信息給NameNode,那么主控服務(wù)器就無(wú)法知道此數(shù)據(jù)服務(wù)器應(yīng)該分配什么數(shù)據(jù)塊應(yīng)該刪除什么數(shù)據(jù)塊,那么它是如何決定的呢?答案就是DatanodeProtocol定義的另一個(gè)方法,blockReport。DataNode也是在主循環(huán)中定時(shí)調(diào)用此方法,只是,其周期通常比調(diào)用sendHeartbeat的更長(zhǎng)。它會(huì)提交本地的所有數(shù)據(jù)塊狀況給NameNode,NameNode會(huì)和本地保存的數(shù)據(jù)塊信息比較,決定什么該刪除什么該新增,并將相關(guān)結(jié)果緩存在本地對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)中,等待此服務(wù)器再發(fā)送sendHeartbeat消息過(guò)來(lái)的時(shí)候,依照這些數(shù)據(jù)結(jié)構(gòu)中的內(nèi)容,做出相應(yīng)的DatanodeCommand指令。blockReport方法同樣也會(huì)返回一個(gè)DatanodeCommand給DataNode,但通常,只是為空(只有出錯(cuò)的時(shí)候不為空),我想,增加緩存,也許是為了確保每個(gè)指令都可以重復(fù)發(fā)送并確定被執(zhí)行。。。
c. 主控服務(wù)器
當(dāng)然,作為整個(gè)系統(tǒng)的核心和單點(diǎn),含辛茹苦的主控服務(wù)器含淚西去,整個(gè)分布式文件服務(wù)集群將徹底癱瘓罷工。如何在主控服務(wù)器犧牲后,提拔新的主控服務(wù)器并迅速使其進(jìn)入工作角色,就成了系統(tǒng)必須考慮的問(wèn)題。解決策略就是:日志。。。
其實(shí)這并不是啥新鮮東西,一看就知道是從數(shù)據(jù)庫(kù)那兒偷師而來(lái)的。在主控服務(wù)器上,所有對(duì)文件目錄操作的關(guān)鍵步驟(具體文件內(nèi)容所處的數(shù)據(jù)服務(wù)器,是不會(huì)被寫(xiě)入日志的,因?yàn)檫@些內(nèi)容是動(dòng)態(tài)建立的...),都會(huì)被寫(xiě)入日志。另外,主控服務(wù)器會(huì)在某些時(shí)刻,將當(dāng)下的文件目錄完整的序列化到本地,這稱(chēng)為鏡像。一旦存有鏡像,鏡像前期所寫(xiě)的日志和其他鏡像,都純屬冗余,其歷史使命已經(jīng)完成,可以報(bào)廢刪除了。在主控服務(wù)器不幸犧牲,或者是戰(zhàn)略性的停機(jī)修整結(jié)束,并重新啟動(dòng)后,主控服務(wù)器會(huì)根據(jù)最近的鏡像 + 鏡像之后的所有日志,重建整個(gè)文件目錄,迅速將服務(wù)能力恢復(fù)到犧牲前的水準(zhǔn)。。。
對(duì)于數(shù)據(jù)服務(wù)器而言,它們會(huì)通過(guò)一些手段,迅速得知頂頭上司的更迭消息。它們會(huì)立刻轉(zhuǎn)投新東家的名下,在新東家旗下注冊(cè),并開(kāi)始向其發(fā)送心跳消息,這個(gè)機(jī)制,可能用分布式協(xié)同服務(wù)來(lái)實(shí)現(xiàn),這里不說(shuō)也罷。。。
在HDFS的實(shí)現(xiàn)中,
FSEditLog類(lèi)是整個(gè)日志體系的核心,提供了一大堆方便的日志寫(xiě)入API,以及日志的恢復(fù)存儲(chǔ)等功能。目前,它支持若干種日志類(lèi)型,都冠以O(shè)P_XXX,并提供相關(guān)API,具體可以參見(jiàn)
這里。為了保證日志的安全性,F(xiàn)SEditLog提供了
EditLogFileOutputStream類(lèi)作為寫(xiě)入的承載類(lèi),它會(huì)同時(shí)開(kāi)若干個(gè)本地文件,然后依次寫(xiě)入,防止日志的損壞導(dǎo)致不可估量的后果。在FSEditLog上面,有一個(gè)
FSImage類(lèi),存儲(chǔ)文件鏡像并調(diào)用FSEditLog對(duì)外提供相關(guān)的日志功能。FSImage是Storage類(lèi)的子類(lèi),如果對(duì)數(shù)據(jù)塊的講述有所印象的話(huà),你可以回憶起來(lái),凡事從此類(lèi)派生出來(lái)的東西,都具有版本性質(zhì),可以進(jìn)行升級(jí)和回滾等等,以此,來(lái)實(shí)現(xiàn)產(chǎn)生鏡像是對(duì)原有日志和鏡像處理的復(fù)雜邏輯。。。
目前,在HDFS的日志系統(tǒng)中,有些地方與GFS的描述有所不同。在HDFS中,所有日志文件和鏡像文件都是本地文件,這就相當(dāng)于,把日志放在自家的保險(xiǎn)箱中,一旦主控服務(wù)器掛了,別的后繼而上的服務(wù)器也無(wú)法拿到這些日志和鏡像,用于重振雄風(fēng)。因此,在HDFS中,運(yùn)行著一個(gè)SecondaryNameNode服務(wù)器,它做為主控服務(wù)器的替補(bǔ),隱忍厚積薄發(fā)為篡位做好準(zhǔn)備,其中,核心內(nèi)容就是:定期下載并處理日志和鏡像。SecondaryNameNode看上去像客戶(hù)端一樣,與NameNode之間,走著NamenodeProtocol協(xié)議。它會(huì)不停的查看主控服務(wù)器上面累計(jì)日志的大小,當(dāng)達(dá)到閾值后,調(diào)用doCheckpoint函數(shù),此函數(shù)的主要步驟包括:
- 首先是調(diào)用startCheckpoint做一些本地的初始化工作;
- 然后調(diào)用rollEditLog,將NameNode上此時(shí)操作的日志文件從edit切到edit.new上來(lái),這個(gè)操作瞬間完成,上層寫(xiě)日志的函數(shù)完全感覺(jué)不到差別;
- 接著,調(diào)用downloadCheckpointFiles,將主控服務(wù)器上的鏡像文件和日志文件都下載到此候補(bǔ)主控服務(wù)器上來(lái);
- 并調(diào)用doMerge,打開(kāi)鏡像和日志,將日志生成新的鏡像,保存覆蓋;
- 下一步,調(diào)用putFSImage把新的鏡像上傳回NameNode;
- 再調(diào)用rollFsImage,將鏡像換成新的,在日志從edit.new改名為edit;
- 最后,調(diào)用endCheckpoint做收尾工作。
整個(gè)算法涉及到NameNode和SecondaryNameNode兩個(gè)服務(wù)器,最終結(jié)果是NameNode和SecondaryNameNode都依照算法進(jìn)行前的日志生成了鏡像。而兩個(gè)服務(wù)器上日志文件的內(nèi)容,前者是整個(gè)算法進(jìn)行期間所寫(xiě)的日志,后者始終不會(huì)有任何日志。當(dāng)主控服務(wù)器犧牲的時(shí)候,運(yùn)行SecondaryNameNode的服務(wù)器立刻被扶正,在其上啟動(dòng)主控服務(wù),利用其日志和鏡像,恢復(fù)文件目錄,并逐步接受各數(shù)據(jù)服務(wù)器的注冊(cè),最終向外提供穩(wěn)定的文件服務(wù)。。。
同樣的事情,GFS采用的可能是另外一個(gè)策略,就是在寫(xiě)日志的時(shí)候,并不局限在本地,而是同時(shí)書(shū)寫(xiě)網(wǎng)絡(luò)日志,即在若干個(gè)遠(yuǎn)程服務(wù)器上生成同樣的日志。然后,在某些時(shí)機(jī),主控服務(wù)器自己,生成鏡像,降低日志規(guī)模。當(dāng)主控服務(wù)器犧牲,可以在擁有網(wǎng)絡(luò)日志的服務(wù)器上啟動(dòng)主控服務(wù),升級(jí)成為主控服務(wù)器。。。
GFS與HDFS的策略相比較,前者是化整為零,后者則是批量處理,通常我們認(rèn)為,批量處理的平均效率更高一些,且相對(duì)而言,可能實(shí)現(xiàn)起來(lái)容易一些,但是,由于有間歇期,會(huì)導(dǎo)致日志的丟失,從而無(wú)法100%的將備份主控服務(wù)器的狀態(tài)與主控服務(wù)器完全同步。。。
2、數(shù)據(jù)的正確性保證
在復(fù)雜紛繁的分布式環(huán)境中,我們堅(jiān)定的相信,萬(wàn)事皆有可能。哪怕各個(gè)服務(wù)器都舒舒服服的活著,也可能有各種各樣的情況導(dǎo)致網(wǎng)絡(luò)傳輸中的數(shù)據(jù)丟失或者錯(cuò)誤。并且在分布式文件系統(tǒng)中,同一份文件的數(shù)據(jù),是存在大量冗余備份的,系統(tǒng)必須要維護(hù)所有的數(shù)據(jù)塊內(nèi)容完全同步,否則,一人一言,不同客戶(hù)端讀同一個(gè)文件讀出不同數(shù)據(jù),用戶(hù)非得瘋了不可。。。
在HDFS中,為了保證數(shù)據(jù)的正確性和同一份數(shù)據(jù)的一致性,做了大量的工作。首先,每一個(gè)數(shù)據(jù)塊,都有一個(gè)版本標(biāo)識(shí),在Block類(lèi)中,用一個(gè)長(zhǎng)整型的數(shù)generationStamp來(lái)表示版本信息(Block類(lèi)是所有表示數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的基類(lèi)),一旦數(shù)據(jù)塊上的數(shù)據(jù)有所變化,此版本號(hào)將向前增加。在主控服務(wù)器上,保存有此時(shí)每個(gè)數(shù)據(jù)塊的版本,一旦出現(xiàn)數(shù)據(jù)服務(wù)器上相關(guān)數(shù)據(jù)塊版本與其不一致,將會(huì)觸發(fā)相關(guān)的恢復(fù)流程。這樣的機(jī)制保證了各個(gè)數(shù)據(jù)服務(wù)器器上的數(shù)據(jù)塊,在基本大方向上都是一致的。但是,由于網(wǎng)絡(luò)的復(fù)雜性,簡(jiǎn)單的版本信息無(wú)法保證具體內(nèi)容的一致性(因?yàn)榇税姹拘畔⑴c內(nèi)容無(wú)關(guān),可能會(huì)出現(xiàn)版本相同,但內(nèi)容不同的狀況)。因此,為了保證數(shù)據(jù)內(nèi)容上的一致,必須要依照內(nèi)容,作出簽名。。。
當(dāng)客戶(hù)端向數(shù)據(jù)服務(wù)器追加寫(xiě)入數(shù)據(jù)包時(shí),每一個(gè)數(shù)據(jù)包的數(shù)據(jù),都會(huì)切分成512字節(jié)大小的段,作為簽名驗(yàn)證的基本單位,在HDFS中,把這個(gè)數(shù)據(jù)段稱(chēng)為Chunk,即傳輸塊(注意,在GFS中,Chunk表達(dá)的是數(shù)據(jù)塊...)。在每一個(gè)數(shù)據(jù)包中,都包含若干個(gè)傳輸塊以及每一個(gè)傳輸塊的簽名,當(dāng)下,這個(gè)簽名是根據(jù)Java SDK提供的CRC算法算得的,其實(shí)就是一個(gè)奇偶校驗(yàn)。當(dāng)數(shù)據(jù)包傳輸?shù)搅魉€(xiàn)的最后一級(jí),數(shù)據(jù)服務(wù)器會(huì)對(duì)其進(jìn)行驗(yàn)證(想一想,為什么只在最后一級(jí)做驗(yàn)證,而不是每級(jí)都做...),一旦發(fā)現(xiàn)當(dāng)前的傳輸塊簽名與在客戶(hù)端中的簽名不一致,整個(gè)數(shù)據(jù)包的寫(xiě)入被視為無(wú)效,Lease Recover(租約恢復(fù))算法被觸發(fā)。。。
從基本原理上看,這個(gè)算法很簡(jiǎn)單,就是取所有數(shù)據(jù)服務(wù)器上此數(shù)據(jù)塊的最小長(zhǎng)度當(dāng)作正確內(nèi)容的長(zhǎng)度,將其他數(shù)據(jù)服務(wù)器上此數(shù)據(jù)塊超出此長(zhǎng)度的部分切除。從正確性上看,此算法無(wú)疑是正確的,因?yàn)橹辽儆幸粋€(gè)數(shù)據(jù)服務(wù)器會(huì)發(fā)現(xiàn)此錯(cuò)誤,并拒絕寫(xiě)入,那么,如果寫(xiě)入了的,都是正確的;從效率上看,此算法也是高效的,因?yàn)樗苊饬酥貜?fù)的傳輸和復(fù)雜的驗(yàn)證,僅僅是各自刪除尾部的一些內(nèi)容即可。但從具體實(shí)現(xiàn)上來(lái)看,此算法稍微有些繞,因?yàn)椋瑸榱私档捅疽巡豢爸刎?fù)的主控服務(wù)器的負(fù)擔(dān),此算法不是由主控服務(wù)器這個(gè)大腦發(fā)起的,而是通過(guò)選舉一個(gè)數(shù)據(jù)服務(wù)器作為Primary,由Primary發(fā)起,通過(guò)調(diào)用與其他各數(shù)據(jù)服務(wù)器間的InterDatanodeProtocol協(xié)議,最終完成的。具體的算法流程,參見(jiàn)LeaseManager類(lèi)上面的注釋。需要說(shuō)明的是此算法的觸發(fā)時(shí)機(jī)和發(fā)起者。此算法可以由客戶(hù)端或者是主控服務(wù)器發(fā)起,當(dāng)客戶(hù)端在寫(xiě)入一個(gè)數(shù)據(jù)包失敗后,會(huì)發(fā)起租約恢復(fù)。因?yàn)椋淮螌?xiě)入失敗,不論是何種原因,很有可能就會(huì)導(dǎo)致流水線(xiàn)上有的服務(wù)器寫(xiě)了,有的沒(méi)寫(xiě),從而造成不統(tǒng)一。而主控服務(wù)器發(fā)起的時(shí)機(jī),則是在占有租約的客戶(hù)端超出一定時(shí)限沒(méi)有續(xù)簽,這說(shuō)明客戶(hù)端可能掛了,在臨死前可能干過(guò)不利于數(shù)據(jù)塊統(tǒng)一的事情,作為監(jiān)督者,主控服務(wù)器需要發(fā)起一場(chǎng)恢復(fù)運(yùn)動(dòng),確保一切正確。。。
3、負(fù)載均衡
負(fù)載的均衡,是分布式系統(tǒng)中一個(gè)永恒的話(huà)題,要讓大家各盡其力齊心干活,發(fā)揮各自獨(dú)特的優(yōu)勢(shì),不能忙得忙死閑得閑死,影響戰(zhàn)斗力。而且,負(fù)載均衡也是一個(gè)復(fù)雜的問(wèn)題,什么是均衡,是一個(gè)很模糊的概念。比如,在分布式文件系統(tǒng)中,總共三百個(gè)數(shù)據(jù)塊,平均分配到十個(gè)數(shù)據(jù)服務(wù)器上,就算均衡了么?其實(shí)不一定,因?yàn)槊恳粋€(gè)數(shù)據(jù)塊需要若干個(gè)備份,各個(gè)備份的分布應(yīng)該充分考慮到機(jī)架的位置,同一個(gè)機(jī)架的服務(wù)器間通信速度更快,而分布在不同機(jī)架則更具有安全性,不會(huì)在一棵樹(shù)上吊死。。。
在這里說(shuō)的負(fù)載均衡,是寬泛意義上的均衡過(guò)程,主要涵蓋兩個(gè)階段的事務(wù),一個(gè)是在任務(wù)初始分配的時(shí)候盡可能合理分配,另一個(gè)是在事后時(shí)刻監(jiān)督及時(shí)調(diào)整。。。
在HDFS中,ReplicationTargetChooser類(lèi),是負(fù)責(zé)實(shí)現(xiàn)為新分配的數(shù)據(jù)塊尋找婆家的。基本上來(lái)說(shuō),數(shù)據(jù)塊的分配工作和備份的數(shù)量、申請(qǐng)的客戶(hù)端地址(也就是寫(xiě)入者)、已注冊(cè)的數(shù)據(jù)服務(wù)器位置,密切相關(guān)。其算法基本思路是只考量靜態(tài)位置信息,優(yōu)先照顧寫(xiě)入者的速度,讓多份備份分配到不同的機(jī)架去。具體算法,自行參見(jiàn)源碼。此外,HDFS的Balancer類(lèi),是為了實(shí)現(xiàn)動(dòng)態(tài)的負(fù)載調(diào)整而存在的。Balancer類(lèi)派生于Tool類(lèi),這說(shuō)明,它是以一個(gè)獨(dú)立的進(jìn)程存在的,可以獨(dú)立的運(yùn)行和配置。它運(yùn)行有NamenodeProtocol和ClientProtocol兩個(gè)協(xié)議,與主控服務(wù)器進(jìn)行通信,獲取各個(gè)數(shù)據(jù)服務(wù)器的負(fù)載狀況,從而進(jìn)行調(diào)整。主要的調(diào)整其實(shí)就是一個(gè)操作,將一個(gè)數(shù)據(jù)塊從一個(gè)服務(wù)器搬遷到另一個(gè)服務(wù)器上。Balancer會(huì)向相關(guān)的目標(biāo)數(shù)據(jù)服務(wù)器發(fā)出一個(gè)DataTransferProtocol.OP_REPLACE_BLOCK消息,接收到這個(gè)消息的數(shù)據(jù)服務(wù)器,會(huì)將數(shù)據(jù)塊寫(xiě)入本地,成功后,通知主控服務(wù)器,刪除早先的那個(gè)數(shù)據(jù)服務(wù)器上的同一塊數(shù)據(jù)塊。具體的算法請(qǐng)自行參考源碼。。。
4、垃圾回收
對(duì)于垃圾,大家應(yīng)該耳熟能詳了,在分布式文件系統(tǒng)而言,沒(méi)有利用價(jià)值的數(shù)據(jù)塊備份,就是垃圾。在現(xiàn)實(shí)生活中,我們提倡垃圾分類(lèi),為了更好的理解分布式文件系統(tǒng)的垃圾收集,搞個(gè)分類(lèi)也是很有必要的。基本上,所有的垃圾都可以視為兩類(lèi),一類(lèi)是由系統(tǒng)正常邏輯產(chǎn)生的,比如某個(gè)文件被刪除了,所有相關(guān)的數(shù)據(jù)塊都淪為垃圾了,某個(gè)數(shù)據(jù)塊被負(fù)載均衡器移動(dòng)了,原始數(shù)據(jù)塊也不幸成了垃圾了。此類(lèi)垃圾最大的特點(diǎn),就是主控服務(wù)器是生成垃圾的罪魁禍?zhǔn)?/strong>,也就是說(shuō)主控服務(wù)器完全了解有哪些垃圾需要處理。另外還有一類(lèi)垃圾,是由于系統(tǒng)的一些異常癥狀產(chǎn)生的,比如某個(gè)數(shù)據(jù)服務(wù)器停機(jī)了一段,重啟之后發(fā)現(xiàn)其上的某個(gè)數(shù)據(jù)塊已經(jīng)在其他服務(wù)器上重新增加了此數(shù)據(jù)塊的備份,它上面的那個(gè)備份過(guò)期了失去價(jià)值了,需要被當(dāng)作垃圾來(lái)處理了。此類(lèi)垃圾的特點(diǎn)恰恰相反,主控服務(wù)器無(wú)法直接了解到垃圾狀況,需要曲線(xiàn)救國(guó)。。。
在HDFS中,第一類(lèi)垃圾的判定自然很容易,在一些正常的邏輯中產(chǎn)生的垃圾,全部被塞進(jìn)了FSNamesystem的recentInvalidateSets這個(gè)Map中。而第二類(lèi)垃圾的判定,則放在數(shù)據(jù)服務(wù)器發(fā)送其數(shù)據(jù)塊信息來(lái)的過(guò)程中,經(jīng)過(guò)與本地信息的比較,可以斷定,此數(shù)據(jù)服務(wù)器上有哪些數(shù)據(jù)塊已經(jīng)不幸淪為垃圾。同樣,這些垃圾也被塞到recentInvalidateSets中去。在與數(shù)據(jù)服務(wù)器進(jìn)行心跳交流的過(guò)程中,主控服務(wù)器會(huì)將它上面有哪些數(shù)據(jù)塊需要?jiǎng)h除,數(shù)據(jù)服務(wù)器對(duì)這些數(shù)據(jù)塊的態(tài)度是,直接物理刪除。在GFS的論文中,對(duì)如何刪除一個(gè)數(shù)據(jù)塊有著不同的理解,它覺(jué)著應(yīng)該先緩存起來(lái),過(guò)幾天沒(méi)人想恢復(fù)它了再刪除。在HDFS的文檔中,則明確表示,在現(xiàn)行的應(yīng)用場(chǎng)景中,沒(méi)有需要這個(gè)需求的地方,因此,直接刪除就完了。這說(shuō)明,理念是一切分歧的根本:)。。。
V. 總結(jié)
整個(gè)分布式文件系統(tǒng),計(jì)算系統(tǒng),數(shù)據(jù)庫(kù)系統(tǒng)的設(shè)計(jì)理念,基本是一脈相承的。三類(lèi)服務(wù)器、作為單點(diǎn)存在的核心控制服務(wù)器、基于日志的恢復(fù)機(jī)制、基于租約的保持聯(lián)系機(jī)制、等等,在后續(xù)分布式計(jì)算系統(tǒng)和分布式數(shù)據(jù)庫(kù)中都可以看到類(lèi)似的影子,在分布式文件系統(tǒng)這里,我詳述了這些內(nèi)容,可能在后續(xù)就會(huì)默認(rèn)知道而說(shuō)的比較簡(jiǎn)略了。而刨去這一些,分布式文件系統(tǒng)中最大特點(diǎn),就是文件塊的冗余存儲(chǔ),它直接導(dǎo)致了較為復(fù)雜的寫(xiě)入流程。當(dāng)然,雖說(shuō)分布式文件系統(tǒng)在分布式計(jì)算和數(shù)據(jù)庫(kù)中都有用到,但如果對(duì)其機(jī)理沒(méi)有興趣,只要把它當(dāng)成是一個(gè)可以在任何機(jī)器上使用的文件系統(tǒng),就不會(huì)對(duì)其他上層建筑的理解產(chǎn)生障礙。。。
首先解釋一下為什么它被稱(chēng)之為SOCKS。其實(shí)該協(xié)議設(shè)計(jì)之初是為了讓有權(quán)限的用戶(hù)可以穿過(guò)過(guò)防火墻的限制,使得高權(quán)限用戶(hù)可以訪(fǎng)問(wèn)一般用戶(hù)不能訪(fǎng)問(wèn)的外部資源。當(dāng)時(shí)設(shè)計(jì)者考慮到幾乎所有使用TCP/IP通信的應(yīng)用軟件都使用socket(套接字,實(shí)際上是一組應(yīng)用程序接口)完成底層的數(shù)據(jù)通信。為了方便軟件開(kāi)發(fā)者使用該協(xié)議,協(xié)議設(shè)計(jì)者就刻意對(duì)應(yīng)了幾組socket編程最經(jīng)典的操作,并且將協(xié)議定名為SOCKS。
最先被廣泛使用的SOCKS協(xié)議是其第四版本,就是SOCKS4。IE和一些其他應(yīng)用程序直接用“Socks”表示SOCKS4協(xié)議。該版本支持TCP的connect(作為客戶(hù)端連接)和listen(打開(kāi)一個(gè)監(jiān)聽(tīng)端口),不支持UDP協(xié)議。SOCKS4A對(duì)SOCKS4作了一點(diǎn)增強(qiáng),即允許客戶(hù)端將域名發(fā)送給SOCKS服務(wù)器,讓SOCKS服務(wù)器進(jìn)行域名解析。
SOCKS5是第五版,相對(duì)第四版作了大幅度的增強(qiáng)。首先,它增加了對(duì)UDP協(xié)議的支持;其次,它可以支持多種用戶(hù)身份驗(yàn)證方式和通信加密方式;最后,修改了SOCKS服務(wù)器進(jìn)行域名解析的方法,使其更加優(yōu)雅。經(jīng)過(guò)這次脫胎換骨的升級(jí),SOCKS5于1996年被IETF確認(rèn)為標(biāo)準(zhǔn)通信協(xié)議,RFC編號(hào)為1928。經(jīng)過(guò)10余年的時(shí)間,大量的網(wǎng)絡(luò)應(yīng)用程序都支持SOCKS5代理。
SOCKS5雖然可以支持多種用戶(hù)身份驗(yàn)證方式,但是應(yīng)用程序真正實(shí)現(xiàn)的一般也只有兩種:不驗(yàn)證和用戶(hù)名密碼驗(yàn)證。所以大多數(shù)應(yīng)用程序SOCKS5代理設(shè)置也只有用戶(hù)名/密碼這一種可選驗(yàn)證方法。另外,盡管從SOCKS4開(kāi)始,就支持打開(kāi)TCP監(jiān)聽(tīng)端口,但是直到SOCKS5,也只允許這個(gè)端口接收一個(gè)客戶(hù)端連接。因此網(wǎng)絡(luò)服務(wù)提供者(如http服務(wù)器)不能使用SOCKS。實(shí)際上,很多SOCKS服務(wù)器的實(shí)現(xiàn)也不支持打開(kāi)TCP監(jiān)聽(tīng)端口。
由于SOCKS5實(shí)際上仍然對(duì)應(yīng)了socket的經(jīng)典操作,所以有人利用這一點(diǎn)編寫(xiě)了一種通用軟件,可以讓不支持SOCKS5協(xié)議的應(yīng)用軟件也能通過(guò)SOCKS5服務(wù)器進(jìn)行網(wǎng)絡(luò)通信,而應(yīng)用軟件則對(duì)此一無(wú)所知。這類(lèi)軟件最著名的莫過(guò)于SocksCap32了,它是Permeo公司(其前身是NEC北美公司的一個(gè)部門(mén),而SOCKS最初就是NEC北美公司的工程師開(kāi)發(fā)并維護(hù)的)早期推出的一款產(chǎn)品。用戶(hù)可以免費(fèi)使用其試用版。試用版和正式版相比,沒(méi)有功能上的限制,只有使用時(shí)間的限制。但是到目前為止,Permeo總是會(huì)在老版本到期之前推出一個(gè)延后了期限的“新”版本,所以用戶(hù)實(shí)際上可以免費(fèi)使用。SocksCap32是利用API鉤子,截獲應(yīng)用軟件對(duì)socket函數(shù)的調(diào)用來(lái)實(shí)現(xiàn)對(duì)SOCKS5客戶(hù)端的模擬。盡管SocksCap32很有名,但是由于推出的時(shí)間較早,對(duì)很多現(xiàn)代應(yīng)用軟件時(shí)常表現(xiàn)的力不從心,所以Permeo又提供了Permeo Security Driver(以下稱(chēng)為PSD)。這款產(chǎn)品使用了驅(qū)動(dòng)技術(shù)從底層直接截獲應(yīng)用軟件的socket通信,因此幾乎可以為所有應(yīng)用軟件提供SOCKS5客戶(hù)端的支持。PSD不提供試用版,但是可以找到其早期版本的注冊(cè)碼。
雖然說(shuō)設(shè)計(jì)SOCKS協(xié)議的初衷是在保證網(wǎng)絡(luò)隔離的情況下,提高部分人員的網(wǎng)絡(luò)訪(fǎng)問(wèn)權(quán)限,但是國(guó)內(nèi)似乎很少有組織機(jī)構(gòu)這樣使用。一般情況下,大家都會(huì)使用更新的網(wǎng)絡(luò)安全技術(shù)來(lái)達(dá)到相同的目的。但是由于SocksCap32和PSD這類(lèi)軟件,人們找到了SOCKS協(xié)議新的用途——突破網(wǎng)絡(luò)通信限制,這和該協(xié)議的初衷實(shí)際上正好相反。比如某些網(wǎng)游的部分服務(wù)器設(shè)置為只接收部分地區(qū)的IP地址的連接。為了突破這種限制,可以找一個(gè)該地區(qū)的SOCKS5代理服務(wù)器,然后用PSD接管網(wǎng)游客戶(hù)端,通過(guò)SOCKS5代理服務(wù)器連接游戲服務(wù)器。這樣游戲服務(wù)器就會(huì)認(rèn)為該客戶(hù)端位于本地區(qū),從而允許進(jìn)行游戲。還有一種情況是:防火墻僅允許部分端口(如http的80端口)通信,那么可以利用SOCKS5協(xié)議和一個(gè)打開(kāi)80端口監(jiān)聽(tīng)的SOCKS5服務(wù)器連接,從而可以連接公網(wǎng)上其他端口的服務(wù)器。利用一些額外的技術(shù)手段,甚至可以騙過(guò)內(nèi)部的http代理服務(wù)器,這時(shí)在使用內(nèi)網(wǎng)http代理上網(wǎng)的環(huán)境下也可以不受限制的使用網(wǎng)絡(luò)服務(wù),這稱(chēng)之為SOCKS over HTTP。通通通([url]www.tongtongtong.com[/url])是老牌SOCKS over HTTP代理提供商,實(shí)現(xiàn)了所有的SOCKS5的連接功能,且有多組國(guó)內(nèi)外服務(wù)器。信天游([url]www.xtyproxy.com[/url]),則是最近剛剛出現(xiàn)的代理服務(wù)提供商,功能和通通通相比還有差距,但是目前完全免費(fèi)。當(dāng)然,使用代理服務(wù)器后,將不可避免的出現(xiàn)通信延遲,所以應(yīng)該盡量選擇同網(wǎng)絡(luò)(指網(wǎng)通/ 電信),距離近的服務(wù)器。
sock5代理的工作程序是:
1.需要向代理方服務(wù)器發(fā)出請(qǐng)求信息。
2.代理方應(yīng)答
3.需要代理方接到應(yīng)答后發(fā)送向代理方發(fā)送目的ip和端口
4.代理方與目的連接
5.代理方將需要代理方發(fā)出的信息傳到目的方,將目的方發(fā)出的信息傳到需要代理方。代理完成。
由于網(wǎng)上的信息傳輸都是運(yùn)用tcp或udp進(jìn)行的,所以使用socks5代理可以辦到網(wǎng)上所能辦到的一切,而且不輿目的方會(huì)查到你的ip,既安全又方
便 sock5支持UDP和TCP,但兩種代理是有區(qū)別的,以下分類(lèi)說(shuō)明
如何用代理TCP協(xié)議
1.向服務(wù)器的1080端口建立tcp連接。
2.向服務(wù)器發(fā)送 05 01 00 (此為16進(jìn)制碼,以下同)
3.如果接到 05 00 則是可以代理
4.發(fā)送 05 01 00 01 + 目的地址(4字節(jié)) + 目的端口(2字節(jié)),目的地址和端口都是16進(jìn)制碼(不是字符串!!)。 例202.103.190.27 -7201 則發(fā)送的信息為:05 01 00 01 CA 67 BE 1B 1C 21 (CA=202 67=103 BE=190 1B=27 1C21=7201)
5.接受服務(wù)器返回的自身地址和端口,連接完成
6.以后操作和直接與目的方進(jìn)行TCP連接相同。
如何用代理UDP連接
1.向服務(wù)器的1080端口建立udp連接
2.向服務(wù)器發(fā)送 05 01 00
3.如果接到 05 00 則是可以代理
4.發(fā)送 05 03 00 01 00 00 00 00 + 本地UDP端口(2字節(jié))
5.服務(wù)器返回 05 00 00 01 +服務(wù)器地址+端口
6.需要申請(qǐng)方發(fā)送 00 00 00 01 +目的地址IP(4字節(jié))+目的端口 +所要發(fā)送的信息
7.當(dāng)有數(shù)據(jù)報(bào)返回時(shí) 向需要代理方發(fā)出00 00 00 01 +來(lái)源地址IP(4字節(jié))+來(lái)源端口 +接受的信息
注:此為不需要密碼的代理協(xié)議,只是socks5的一部分,完整協(xié)議請(qǐng)RFC1928
SOCKS V5的用戶(hù)名/密碼鑒定
(RFC1929 Username/Password Authentication for SOCKS V5)
本備忘錄狀態(tài):
本文檔講述了一種Internet社區(qū)的Internet標(biāo)準(zhǔn)跟蹤協(xié)議,它需要進(jìn)一步進(jìn)行討論和建議以得到改進(jìn)。請(qǐng)參考最新版的“Internet正式協(xié)議標(biāo)準(zhǔn)” (STD1)來(lái)獲得本協(xié)議的標(biāo)準(zhǔn)化程度和狀態(tài)。本備忘錄的發(fā)布不受任何限制。
1. 介紹
關(guān)于SOCKS V5的協(xié)議規(guī)范說(shuō)明了在初始化SOCKS連接時(shí)所用到的任意驗(yàn)證協(xié)議的大致框架。這篇文檔描述了這些協(xié)議中的其中一個(gè)適合SOCKS V5驗(yàn)證子協(xié)商(subnegotiation)。
注意:
除非特別注明,所有出現(xiàn)在數(shù)據(jù)包格式圖中的十進(jìn)制數(shù)字均以字節(jié)表示相應(yīng)域的長(zhǎng)度。如果某域需要給定一個(gè)字節(jié)的值,用X’hh’來(lái)表示這個(gè)字節(jié)中的值。如果某域中用到單詞’Variable’,這表示該域的長(zhǎng)度是可變的,且該長(zhǎng)度定義在一個(gè)和這個(gè)域相關(guān)聯(lián)(1 – 2個(gè)字節(jié))的域中,或一個(gè)數(shù)據(jù)類(lèi)型域中。
2.初始協(xié)商
一旦SOCKS V5服務(wù)器運(yùn)行并且客戶(hù)端選擇了用戶(hù)名/密碼認(rèn)證協(xié)議以后,就開(kāi)始了用戶(hù)名/密碼協(xié)議的子協(xié)商過(guò)程。客戶(hù)端先產(chǎn)生一個(gè)用戶(hù)名/密碼協(xié)議的請(qǐng)求:
| VER |
ULEN |
UNAME |
PLEN |
PASSWD |
| 1 |
1 |
1 to 255 |
1 |
1 to 255 |
VER中指明了子協(xié)商的當(dāng)前版本,現(xiàn)在使用的是X’01’。ULEN域中包含了下一個(gè)UNAME域的長(zhǎng)度。UNAME中包含一個(gè)源操作系統(tǒng)(source operating system)所知道的用戶(hù)名。PLEN中指明了緊隨其后的PASSWD的長(zhǎng)度。PASSWD中則包含了對(duì)應(yīng)UNAME用戶(hù)的密碼。
服務(wù)器驗(yàn)證用戶(hù)名和密碼,并且返回:
如果STATUS中返回X’00’則說(shuō)明通過(guò)驗(yàn)證。如果服務(wù)器返回非X’00’則說(shuō)明驗(yàn)證失敗,并且關(guān)閉連接。
3.安全考慮
這篇文檔描述了為SOCKS V5協(xié)議提供驗(yàn)證服務(wù)的子協(xié)商過(guò)程。因?yàn)槊艽a是以明文傳輸?shù)模赃@個(gè)子協(xié)商過(guò)程在可能被工具“嗅探(sniffing)”到的環(huán)境中不建議使用該子協(xié)商過(guò)程。
譯者:Radeon(Radeon bise@cmmail.com)
譯文發(fā)布時(shí)間:2001-6-18
目錄
1.介紹
2.現(xiàn)有的協(xié)議
3.基于TCP協(xié)議的客戶(hù)
4.請(qǐng)求
5.地址
6.應(yīng)答
7.基于UDP協(xié)議的客戶(hù)
8. 安全性考慮
9. 參考書(shū)目
1.介紹
利用網(wǎng)絡(luò)防火墻可以將組織內(nèi)部的網(wǎng)絡(luò)結(jié)構(gòu)從外部網(wǎng)絡(luò)如INTERNET中有效地隔離,這種方法在許多網(wǎng)絡(luò)系統(tǒng)中正變得流行起來(lái)。這種防火墻系統(tǒng)通常以應(yīng)用層網(wǎng)關(guān)的形式工作在兩個(gè)網(wǎng)絡(luò)之間,提供TELNET、FTP、SMTP等的接入。隨著越來(lái)越多的使全球信息查找更容易的復(fù)雜的應(yīng)用層協(xié)議的出現(xiàn),有必要提供一個(gè)通用框架來(lái)使這些協(xié)議安全透明地穿過(guò)防火墻。而且在實(shí)際應(yīng)用中還需要一種安全的認(rèn)證方式用以穿越防火墻。這個(gè)要求起源于兩個(gè)組織的網(wǎng)絡(luò)中客戶(hù)/服務(wù)器關(guān)系的出現(xiàn),這個(gè)關(guān)系需要得到控制并要求有安全的認(rèn)證。
在這兒所描述的協(xié)議框架是為了讓使用TCP和UDP的客戶(hù)/服務(wù)器應(yīng)用程序更方便安全地使用網(wǎng)絡(luò)防火墻所提供的服務(wù)所設(shè)計(jì)的。這個(gè)協(xié)議從概念上來(lái)講是介于應(yīng)用層和傳輸層之間的“中介層(shim-layer)”,因而不提供如傳遞ICMP信息之類(lèi)由網(wǎng)絡(luò)層網(wǎng)關(guān)的所提供的服務(wù)。
2.現(xiàn)有的協(xié)議
當(dāng)前存在一個(gè)協(xié)議SOCKS 4,它為T(mén)ELNET、FTP、HTTP、WAIS和GOPHER等基于TCP協(xié)議的客戶(hù)/服務(wù)器程序提供了一個(gè)不安全的防火墻。而這個(gè)新的協(xié)議擴(kuò)展了SOCKS V4,以使其支持UDP、框架規(guī)定的安全認(rèn)證方案、地址解析方案(addressing scheme)中所規(guī)定的域名和IPV6。為了實(shí)現(xiàn)這個(gè)SOCKS協(xié)議,通常需要重新編譯或者重新鏈接基于TCP的客戶(hù)端應(yīng)用程序以使用SOCKS庫(kù)中相應(yīng)的加密函數(shù)。
注意:
除非特別注明,所有出現(xiàn)在數(shù)據(jù)包格式圖中的十進(jìn)制數(shù)字均以字節(jié)表示相應(yīng)域的長(zhǎng)度。如果某域需要給定一個(gè)字節(jié)的值,用X’hh’來(lái)表示這個(gè)字節(jié)中的值。如果某域中用到單詞’Variable’,這表示該域的長(zhǎng)度是可變的,且該長(zhǎng)度定義在一個(gè)和這個(gè)域相關(guān)聯(lián)(1 – 2個(gè)字節(jié))的域中,或一個(gè)數(shù)據(jù)類(lèi)型域中。
3.基于TCP協(xié)議的客戶(hù)
當(dāng)一個(gè)基于TCP協(xié)議的客戶(hù)端希望與一個(gè)只能通過(guò)防火墻可以到達(dá)的目標(biāo)(這是由實(shí)現(xiàn)所決定的)建立連接,它必須先建立一個(gè)與SOCKS服務(wù)器上SOCKS端口的TCP連接。通常這個(gè)TCP端口是1080。當(dāng)連接建立后,客戶(hù)端進(jìn)入?yún)f(xié)議的“握手(negotiation)”過(guò)程:認(rèn)證方式的選擇,根據(jù)選中的方式進(jìn)行認(rèn)證,然后發(fā)送轉(zhuǎn)發(fā)的要求。SOCKS服務(wù)器檢查這個(gè)要求,根據(jù)結(jié)果,或建立合適的連接,或拒絕。
除非特別注明,所有出現(xiàn)在數(shù)據(jù)包格式圖中的十進(jìn)制數(shù)字均以字節(jié)表示相應(yīng)域的長(zhǎng)度。如果某域需要給定一個(gè)字節(jié)的值,用X’hh’來(lái)表示這個(gè)字節(jié)中的值。如果某域中用到單詞’Variable’,這表示該域的長(zhǎng)度是可變的,且該長(zhǎng)度定義在一個(gè)和這個(gè)域相關(guān)聯(lián)(1 – 2個(gè)字節(jié))的域中,或一個(gè)數(shù)據(jù)類(lèi)型域中。
客戶(hù)端連到服務(wù)器后,然后就發(fā)送請(qǐng)求來(lái)協(xié)商版本和認(rèn)證方法:
| VER |
NMETHODS |
METHODS |
| 1 |
1 |
1 to 255 |
這個(gè)版本的SOCKS協(xié)議中,VER字段被設(shè)置成X'05'。NMETHODS字段包含了在METHODS字段中出現(xiàn)的方法標(biāo)示的數(shù)目(以字節(jié)為單位)。
服務(wù)器從這些給定的方法中選擇一個(gè)并發(fā)送一個(gè)方法選中的消息回客戶(hù)端:
如果選中的消息是X’FF’,這表示客戶(hù)端所列出的方法列表中沒(méi)有一個(gè)方法被選中,客戶(hù)端必須關(guān)閉連接。
當(dāng)前定義的方法有:
· X’00’ 不需要認(rèn)證
· X’01’ GSSAPI
· X’02’ 用戶(hù)名/密碼
· X’03’ -- X’7F’ 由IANA分配
· X’80’ -- X’FE’ 為私人方法所保留的
· X’FF’ 沒(méi)有可以接受的方法
然后客戶(hù)和服務(wù)器進(jìn)入由選定認(rèn)證方法所決定的子協(xié)商過(guò)程(sub-negotiation)。各種不同的方法的子協(xié)商過(guò)程的描述請(qǐng)參考各自的備忘錄。
開(kāi)發(fā)者如果要為自己的方法得到一個(gè)方法號(hào),可以聯(lián)系IANA。可以參考關(guān)于已經(jīng)被分配號(hào)碼的文檔以得到當(dāng)前所有方法的列表和相應(yīng)的協(xié)議。
符合本文檔的SOCKS V5實(shí)現(xiàn)必須支持GSSAPI,并且在將來(lái)支持用戶(hù)名/密碼認(rèn)證方式。
4.請(qǐng)求
一旦子協(xié)商過(guò)程結(jié)束后,客戶(hù)端就發(fā)送詳細(xì)的請(qǐng)求信息。如果協(xié)商的方法中有以完整性檢查和/或安全性為目的的封裝,這些請(qǐng)求必須按照該方法所定義的方式進(jìn)行封裝。
SOCKS請(qǐng)求的格式如下:
| VER |
CMD |
RSV |
ATYP |
DST.ADDR |
DST.PROT |
| 1 |
1 |
X’00’ |
1 |
Variable |
2 |
其中
· VER 協(xié)議版本: X’05’
· CMD
· CONNECT:X’01’
· BIND:X’02’
· UDP ASSOCIATE:X’03’
· RSV 保留
· ATYP 后面的地址類(lèi)型
· IPV4:X’01’
· 域名:X’03’
· IPV6:X’04’'
· DST.ADDR 目的地址
· DST.PORT 以網(wǎng)絡(luò)字節(jié)順序出現(xiàn)的端口號(hào)
SOCKS服務(wù)器會(huì)根據(jù)源地址和目的地址來(lái)分析請(qǐng)求,然后根據(jù)請(qǐng)求類(lèi)型返回一個(gè)或多個(gè)應(yīng)答。
5.地址
ATYP字段中描述了地址字段(DST.ADDR,BND.ADDR)所包含的地址類(lèi)型:
· X'01'
基于IPV4的IP地址,4個(gè)字節(jié)長(zhǎng)
· X'03'
基于域名的地址,地址字段中的第一字節(jié)是以字節(jié)為單位的該域名的長(zhǎng)度,沒(méi)有結(jié)尾的NUL字節(jié)。
· X'04'
基于IPV6的IP地址,16個(gè)字節(jié)長(zhǎng)
6.應(yīng)答
一旦建立了一個(gè)到SOCKS服務(wù)器的連接,并且完成了認(rèn)證方式的協(xié)商過(guò)程,客戶(hù)機(jī)將會(huì)發(fā)送一個(gè)SOCKS請(qǐng)求信息給服務(wù)器。服務(wù)器將會(huì)根據(jù)請(qǐng)求,以如下格式返回:
| VER |
REP |
RSV |
ATYP |
BND.ADDR |
BND.PORT |
| 1 |
1 |
X’00’ |
1 |
Variable |
2 |
其中:
· VER 協(xié)議版本: X’05’
· REP 應(yīng)答字段:
· X’00’ 成功
· X’01’ 普通的SOCKS服務(wù)器請(qǐng)求失敗
· X’02’ 現(xiàn)有的規(guī)則不允許的連接
· X’03’ 網(wǎng)絡(luò)不可達(dá)
· X’04’ 主機(jī)不可達(dá)
· X’05’ 連接被拒
· X’06’ TTL超時(shí)
· X’07’ 不支持的命令
· X’08’ 不支持的地址類(lèi)型
· X’09’ – X’FF’ 未定義
· RSV 保留
· ATYP 后面的地址類(lèi)型
· IPV4:X’01’
· 域名:X’03’
· IPV6:X’04’
· BND.ADDR 服務(wù)器綁定的地址
· BND.PORT 以網(wǎng)絡(luò)字節(jié)順序表示的服務(wù)器綁定的段口
標(biāo)識(shí)為RSV的字段必須設(shè)為X’00’。
如果選中的方法中有以完整性檢查和/或安全性為目的的封裝,這些應(yīng)答必須按照該方法所定義的方式進(jìn)行封裝。
CONNECT
在對(duì)一個(gè)CONNECT命令的應(yīng)答中,BND.PORT包含了服務(wù)器分配的用來(lái)連到目標(biāo)機(jī)的端口號(hào),BND.ADDR則是相應(yīng)的IP地址。由于SOCKS服務(wù)器通常有多個(gè)IP,應(yīng)答中的BND.ADDR常和客戶(hù)端連到SOCKS服務(wù)器的那個(gè)IP不同。
SOCKS服務(wù)器可以利用DST.ADDR和DST.PORT,以及客戶(hù)端源地址和端口來(lái)對(duì)一個(gè)CONNECT請(qǐng)求進(jìn)行分析。
BIND
BIND請(qǐng)求通常被用在那些要求客戶(hù)端接受來(lái)自服務(wù)器的連接的協(xié)議上。FTP是一個(gè)典型的例子。它建立一個(gè)從客戶(hù)端到服務(wù)器端的連接來(lái)執(zhí)行命令以及接收狀態(tài)的報(bào)告,而使用另一個(gè)從服務(wù)器到客戶(hù)端的連接來(lái)接收傳輸數(shù)據(jù)的要求(如LS,GET,PUT)。
建議只有在一個(gè)應(yīng)用協(xié)議的客戶(hù)端在使用CONNECT命令建立主連接后才可以使用BIND命令建立第二個(gè)連接。建議SOCKS服務(wù)器使用DST.ADDR和DST.PORT來(lái)評(píng)價(jià)BIND請(qǐng)求。
在一個(gè)BIND請(qǐng)求的操作過(guò)程中,SOCKS服務(wù)器要發(fā)送兩個(gè)應(yīng)答給客戶(hù)端。當(dāng)服務(wù)器建立并綁定一個(gè)新的套接口時(shí)發(fā)送第一個(gè)應(yīng)答。BND.PORT字段包含SOCKS服務(wù)器用來(lái)監(jiān)聽(tīng)進(jìn)入的連接的端口號(hào),BAND.ADDR字段包含了對(duì)應(yīng)的IP地址。客戶(hù)端通常使用這些信息來(lái)告訴(通過(guò)主連接或控制連接)應(yīng)用服務(wù)器連接的匯接點(diǎn)。第二個(gè)應(yīng)答僅發(fā)生在所期望到來(lái)的連接成功或失敗之后。在第二個(gè)應(yīng)答中,BND.PORT和BND.ADDR字段包含了連上來(lái)的主機(jī)的IP地址和端口號(hào)。
UDP ASSOCIATE
UDP ASSOCIATE請(qǐng)求通常是要求建立一個(gè)UDP轉(zhuǎn)發(fā)進(jìn)程來(lái)控制到來(lái)的UDP數(shù)據(jù)報(bào)。DST.ADDR和DST.PORT 字段包含客戶(hù)端所希望的用來(lái)發(fā)送UDP數(shù)據(jù)報(bào)的IP地址和端口號(hào)。服務(wù)器可以使用這個(gè)信息來(lái)限制進(jìn)入的連接。如果客戶(hù)端在發(fā)送這個(gè)請(qǐng)求時(shí)沒(méi)有地址和端口信息,客戶(hù)端必須用全0來(lái)填充。
當(dāng)與UDP相應(yīng)的TCP連接中斷時(shí),該UDP連接也必須中斷。
應(yīng)答UDP ASSOCIATE請(qǐng)求時(shí),BND.PORT 和BND.ADDR字段指明了客戶(hù)發(fā)送UDP消息至服務(wù)器的端口和地址。
應(yīng)答處理
當(dāng)一個(gè)應(yīng)答(REP值不等于00)指明出錯(cuò)時(shí),SOCKS服務(wù)器必須在發(fā)送完應(yīng)答消息后一小段時(shí)間內(nèi)終止TCP連接。這段時(shí)間應(yīng)該在發(fā)現(xiàn)錯(cuò)誤后少于10秒。
如果一個(gè)應(yīng)答(REP值等于00)指明成功,并且請(qǐng)求是一個(gè)BIND或CONNECT時(shí),客戶(hù)端就可以開(kāi)始發(fā)送數(shù)據(jù)了。如果協(xié)商的認(rèn)證方法中有以完整性、認(rèn)證和/或安全性為目的的封裝,這些請(qǐng)求必須按照該方法所定義的方式進(jìn)行封裝。類(lèi)似的,當(dāng)以客戶(hù)機(jī)為目的地的數(shù)據(jù)到達(dá)SOCKS服務(wù)器時(shí),SOCKS服務(wù)器必須用正在使用的方法對(duì)這些數(shù)據(jù)進(jìn)行封裝。
7.基于UDP協(xié)議的客戶(hù)
在UDP ASSOCIATE應(yīng)答中由BND.PORT指明了服務(wù)器所使用的UDP端口,一個(gè)基于UDP協(xié)議的客戶(hù)必須發(fā)送數(shù)據(jù)報(bào)至UDP轉(zhuǎn)發(fā)服務(wù)器的該端口上。如果協(xié)商的認(rèn)證方法中有以完整性、認(rèn)證和/或安全性為目的的封裝,這些數(shù)據(jù)報(bào)必須按照該方法所定義的方式進(jìn)行封裝。每個(gè)UDP數(shù)據(jù)報(bào)都有一個(gè)UDP請(qǐng)求頭在其首部:
| RSV |
FRAG |
ATYP |
DST.ADDR |
DST.PORT |
DATA |
| 2 |
1 |
1 |
Variable |
2 |
Variable |
在UDP請(qǐng)求頭中的字段是:
· RSV 保留 X’0000’
· FRAG 當(dāng)前的分段號(hào)
· ATYP 后面的地址類(lèi)型
· IPV4:X’01’
· 域名:X’03’
· IPV6:X’04’
· DST.ADDR 目的地址
· DST.PORT 以網(wǎng)絡(luò)字節(jié)順序出現(xiàn)的端口號(hào)
· DATA 用戶(hù)數(shù)據(jù)
當(dāng)一個(gè)UDP轉(zhuǎn)發(fā)服務(wù)器轉(zhuǎn)發(fā)一個(gè)UDP數(shù)據(jù)報(bào)時(shí),不會(huì)發(fā)送任何通知給客戶(hù)端;同樣,它也將丟棄任何它不能發(fā)至遠(yuǎn)端主機(jī)的數(shù)據(jù)報(bào)。當(dāng)UDP轉(zhuǎn)發(fā)服務(wù)器從遠(yuǎn)端服務(wù)器收到一個(gè)應(yīng)答的數(shù)據(jù)報(bào)時(shí),必須加上上述UDP請(qǐng)求頭,并對(duì)數(shù)據(jù)報(bào)進(jìn)行封裝。
UDP轉(zhuǎn)發(fā)服務(wù)器必須從SOCKS服務(wù)器得到期望的客戶(hù)端IP地址,并將數(shù)據(jù)報(bào)發(fā)送到UDP ASSOCIATE應(yīng)答中給定的端口號(hào)。如果數(shù)據(jù)報(bào)從任何IP地址到來(lái),而該IP地址與該特定連接中指定的IP地址不同,那么該數(shù)據(jù)報(bào)會(huì)被丟棄。
FRAG字段指明數(shù)據(jù)報(bào)是否是一些分片中的一片。如果SOCKS服務(wù)器要實(shí)現(xiàn)這個(gè)功能,X’00’指明數(shù)據(jù)報(bào)是獨(dú)立的;其他則越大越是數(shù)據(jù)報(bào)的尾端。介于1到127之間的值說(shuō)明了該分片在分片序列里的位置。每個(gè)接收者都為這些分片提供一個(gè)重組隊(duì)列和一個(gè)重組的計(jì)時(shí)器。這個(gè)重組隊(duì)列必須在重組計(jì)時(shí)器超時(shí)后重新初始化,并丟棄相應(yīng)的數(shù)據(jù)報(bào)。或者當(dāng)一個(gè)新到達(dá)的數(shù)據(jù)報(bào)有一個(gè)比當(dāng)前在處理的數(shù)據(jù)報(bào)序列中最大的FRAG值要小時(shí),也必須重新初始化從組隊(duì)列。重組計(jì)時(shí)器必須小于5秒。只要有可能,應(yīng)用程序最好不要使用分片。
分片的實(shí)現(xiàn)是可選的;如果某實(shí)現(xiàn)不支持分片,所有FRAG字段不為0的數(shù)據(jù)報(bào)都必須被丟棄。
一個(gè)SOCKS的UDP編程界面(The programming interface for a SOCKS-aware UDP)必須報(bào)告當(dāng)前可用UDP數(shù)據(jù)報(bào)緩存空間小于操作系統(tǒng)提供的實(shí)際空間。
· 如果 ATYP是 X’01’ - 10+method_dependent octets smaller
· 如果 ATYP是X’03’ - 262+method_dependent octets smaller
· 如果 ATYP是X’04’ - 20+method_dependent octets smaller
8. 安全性考慮
這篇文檔描述了一個(gè)用來(lái)透過(guò)IP網(wǎng)絡(luò)防火墻的應(yīng)用層協(xié)議。這種傳輸?shù)陌踩栽诤艽蟪潭壬弦蕾?lài)于特定實(shí)現(xiàn)所擁有以及在SOCKS客戶(hù)與SOCKS服務(wù)器之間經(jīng)協(xié)商所選定的特殊的認(rèn)證和封裝方式。
系統(tǒng)管理員需要對(duì)用戶(hù)認(rèn)證方式的選擇進(jìn)行仔細(xì)考慮。
1.安裝程序準(zhǔn)備
你需要準(zhǔn)備TortoiseSVN-1.5.5.14361-win32-svn-1.5.4.msi和VisualSVN-1.5.4.msi兩個(gè)安裝程序,其中TortoiseSVN-1.5.5.14361-win32-svn-1.5.4.msi安裝之后主要用于察看和管理使用;VisualSVN-1.5.4.msi主要是為了VS2005使用。
先安裝TortoiseSVN-1.5.5.14361-win32-svn-1.5.4.msi,一直下一步到底,然后安裝VisualSVN-1.5.4.msi一直下一步到底,
提示:如果你不使用VS2005,那么不需要安裝VisualSVN-1.5.4.msi2.破解VisualSVN由于默認(rèn)安裝VisualSVN-1.5.4.msi只有29天使用時(shí)限,下面說(shuō)破解方法:
提示:只針對(duì)于VisualSVN 1.5.x第一步:首先去認(rèn)你系統(tǒng)安裝了.NET Framework;
第二步:進(jìn)入.NET Framework命令提示符,輸入或者直接復(fù)制:
ildasm "C:\Program Files\VisualSVN\bin\VisualSVN.Core.dll" /out="C:\Program Files\VisualSVN\bin\VisualSVN.Core.il"
第三步: 切換到“C:\Program Files\VisualSVN\bin\”目錄,使用文本編輯器打開(kāi)剛才輸出的il文件,查找
.method public hidebysig static bool IsValid(
將該方法括號(hào)(
{})內(nèi)的代碼體替換成
.maxstack 8

IL_0000: ldc.i4.1

IL_0001: ret
第四步: 回到命令行輸入
ilasm "C:\Program Files\VisualSVN\bin\VisualSVN.Core.il" /dll編譯得到新的dll覆蓋原VisualSVN.Core.dll,默認(rèn)執(zhí)行后就是覆蓋了。
第五步: 破解完畢,打開(kāi)VS.NET,點(diǎn)擊VisualSVN菜單->Registration,輸入任意字符點(diǎn)擊OK注冊(cè)成功。
在你所要設(shè)為自增型的鍵上(比如你的id)雙擊,彈出一個(gè)Column Properties對(duì)話(huà)框,右下角有一個(gè)Identify的選擇框,選中它OK,就可以了。
再去查看Preview,就能看到用大寫(xiě)標(biāo)識(shí)出來(lái)的AUTO_INCREMENT。
在RH Linux下修改IP、DNS和路由配置
ifconfig eth0 新ip
然后編輯/etc/sysconfig/network-scripts/ifcfg-eth0,修改ip
一、修改IP地址
[aeolus@db1 network-scripts]$ vi ifcfg-eth0
DEVICE=eth0
ONBOOT=yes
BOOTPROTO=static
IPADDR=219.136.241.211
NETMASK=255.255.255.128
GATEWAY=219.136.241.254
二、修改網(wǎng)關(guān)
vi /etc/sysconfig/network
NETWORKING=yes
HOSTNAME=Aaron
GATEWAY=192.168.1.1
三、修改DNS
[aeolus@db1 etc]$ vi resolv.conf
nameserver 202.96.128.68
nameserver 219.136.241.206
四、重新啟動(dòng)網(wǎng)絡(luò)配置
/etc/init.d/network restart
修改ip地址
即時(shí)生效:
# ifconfig eth0 192.168.0.20 netmask 255.255.255.0
啟動(dòng)生效:
修改/etc/sysconfig/network-scripts/ifcfg-eth0
修改default gateway
即時(shí)生效:
# route add default gw 192.168.0.254
啟動(dòng)生效:
修改/etc/sysconfig/network-scripts/ifcfg-eth0
修改dns
修改/etc/resolv.conf
修改后可即時(shí)生效,啟動(dòng)同樣有效
修改host name
即時(shí)生效:
# hostname fc2
啟動(dòng)生效:
修改/etc/sysconfig/network.