1. 程式人生 > >Hadoop詳解(二)——HDFS的命令,執行過程,Java介面,原理詳解。RPC機制

Hadoop詳解(二)——HDFS的命令,執行過程,Java介面,原理詳解。RPC機制

HDFS是Hadoop的一大核心,關於HDFS需要掌握的有:

分散式系統與HDFS、HDFS的體系架構和基本概念、HDFS的shell操作、Java介面以及常用的API、Hadoop的RPC機制、遠端debug

Distributed  File System

資料量越來越多,在一個作業系統管理的範圍儲存不了,那麼就需要分配到更多的作業系統管理的磁碟中,但是不方便管理和維護,因此迫切需要一種系統來管理多臺機器上的檔案,這就是所謂的分散式檔案管理系統。分散式檔案管理系統是一種允許檔案通過網路在多臺機器主機上分享的檔案系統,可以讓多臺機器上的多使用者分享檔案和儲存空間。通透性。實際上通過網路來訪問檔案的動作,由程式和使用者看來,就像是訪問本地的磁碟一般。
容錯。即使系統中有些節點離線,整體來說系統仍然可以持續運作而不會有資料丟失。分散式檔案系統有很多,HDFS只是其中的一種。適用於一次寫入多次查詢,不支援併發寫情況,小檔案不合適。(注意:分散式檔案系統不支援併發寫並不是指禁止多個客戶端同時寫資料,而是指在寫一個檔案時不允許同時向多個塊進行寫入)常見的分散式檔案系統有很多如下所示:常見的分散式檔案系統有,GFS、HDFS、Lustre 、Ceph 、GridFS 、mogileFS、TFS、FastDFS等。各自適用於不同的領域。它們都不是系統級的分散式檔案系統,而是應用級的分散式檔案儲存服務。
Google學術論文,這是眾多分散式檔案系統的起源
==================================
Google File System(大規模分散檔案系統)
MapReduce (大規模分散FrameWork)
BigTable(大規模分散資料庫)
Chubby(分散鎖服務)
一般你搜索Google_三大論文中文版(Bigtable、 GFS、 Google MapReduce)就有了。
做箇中文版下載源:http://dl.iteye.com/topics/download/38db9a29-3e17-3dce-bc93-df9286081126
做個原版地址連結:
http://labs.google.com/papers/gfs.html
http://labs.google.com/papers/bigtable.html 
http://labs.google.com/papers/mapreduce.html

GFS(Google File System)
Google公司為了滿足本公司需求而開發的基於Linux的專有分散式檔案系統。。儘管Google公佈了該系統的一些技術細節,但Google並沒有將該系統的軟體部分作為開源軟體釋出。
下面分散式檔案系統都是類 GFS的產品。

HDFS
Hadoop 實現了一個分散式檔案系統(Hadoop Distributed File System),簡稱HDFS。 Hadoop是Apache Lucene創始人Doug Cutting開發的使用廣泛的文字搜尋庫。它起源於Apache Nutch,後者是一個開源的網路搜尋引擎,本身也是Luene專案的一部分。Aapche Hadoop架構是MapReduce演算法的一種開源應用,是Google開創其帝國的重要基石。

Ceph
是加州大學聖克魯茲分校的Sage weil攻讀博士時開發的分散式檔案系統。並使用Ceph完成了他的論文。
說 ceph 效能最高,C++編寫的程式碼,支援Fuse,並且沒有單點故障依賴, 於是下載安裝, 由於 ceph 使用 btrfs 檔案系統, 而btrfs 檔案系統需要 Linux 2.6.34 以上的核心才支援。
可是ceph太不成熟了,它基於的btrfs本身就不成熟,它的官方網站上也明確指出不要把ceph用在生產環境中。

Lustre
Lustre是一個大規模的、安全可靠的,具備高可用性的叢集檔案系統,它是由SUN公司開發和維護的。
該專案主要的目的就是開發下一代的叢集檔案系統,可以支援超過10000個節點,數以PB的資料量儲存系統。
目前Lustre已經運用在一些領域,例如HP SFS產品等。
適合儲存小檔案、圖片的分佈檔案系統研究
用於圖片等小檔案大規模儲存的分散式檔案系統調研
架構高效能海量圖片伺服器的技術要素
nginx效能改進一例(圖片全部存入google的leveldb)
FastDFS分佈檔案系統
TFS(Taobao File System)安裝方法
動態生成圖片 Nginx + GraphicsMagick 

MogileFS
由memcahed的開發公司danga一款perl開發的產品,目前國內使用mogielFS的有圖片託管網站yupoo等。
MogileFS是一套高效的檔案自動備份元件,由Six Apart開發,廣泛應用在包括LiveJournal等web2.0站點上。
MogileFS由3個部分組成:
  第1個部分是server端,包括mogilefsd和mogstored兩個程式。前者即是 mogilefsd的tracker,它將一些全域性資訊儲存在資料庫裡,例如站點domain,class,host等。後者即是儲存節點(store node),它其實是個HTTP Daemon,預設偵聽在7500埠,接受客戶端的檔案備份請求。在安裝完後,要執行mogadm工具將所有的store node註冊到mogilefsd的資料庫裡,mogilefsd會對這些節點進行管理和監控。
  第2個部分是utils(工具集),主要是MogileFS的一些管理工具,例如mogadm等。
  第3個部分是客戶端API,目前只有Perl API(MogileFS.pm)、PHP,用這個模組可以編寫客戶端程式,實現檔案的備份管理功能。

mooseFS
持FUSE,相對比較輕量級,對master伺服器有單點依賴,用perl編寫,效能相對較差,國內用的人比較多
MooseFS與MogileFS的效能測試對比 
 
FastDFS
是一款類似Google FS的開源分散式檔案系統,是純C語言開發的。
FastDFS是一個開源的輕量級分散式檔案系統,它對檔案進行管理,功能包括:檔案儲存、檔案同步、檔案訪問(檔案上傳、檔案下載)等,解決了大容量儲存和負載均衡的問題。特別適合以檔案為載體的線上服務,如相簿網站、視訊網站等等。
官方論壇  http://bbs.chinaunix.net/forum-240-1.html
FastDfs google Code     http://code.google.com/p/fastdfs/
分散式檔案系統FastDFS架構剖析   http://www.programmer.com.cn/4380/
 
TFS
TFS(Taobao !FileSystem)是一個高可擴充套件、高可用、高效能、面向網際網路服務的分散式檔案系統,主要針對海量的非結構化資料,它構築在普通的Linux機器 叢集上,可為外部提供高可靠和高併發的儲存訪問。TFS為淘寶提供海量小檔案儲存,通常檔案大小不超過1M,滿足了淘寶對小檔案儲存的需求,被廣泛地應用 在淘寶各項應用中。它採用了HA架構和平滑擴容,保證了整個檔案系統的可用性和擴充套件性。同時扁平化的資料組織結構,可將檔名對映到檔案的實體地址,簡化 了檔案的訪問流程,一定程度上為TFS提供了良好的讀寫效能。
官網 : http://code.taobao.org/p/tfs/wiki/index/

GridFS檔案系統
MongoDB是一種知名的NoSql資料庫,GridFS是MongoDB的一個內建功能,它提供一組檔案操作的API以利用MongoDB儲存檔案,GridFS的基本原理是將檔案儲存在兩個Collection中,一個儲存檔案索引,一個儲存檔案內容,檔案內容按一定大小分成若干塊,每一塊存在一個Document中,這種方法不僅提供了檔案儲存,還提供了對檔案相關的一些附加屬性(比如MD5值,檔名等等)的儲存。檔案在GridFS中會按4MB為單位進行分塊儲存。
MongoDB GridFS 資料讀取效率 benchmark
http://blog.nosqlfan.com/html/730.html
nginx + gridfs 實現圖片的分散式儲存  安裝(一年後出問題了)
http://www.cnblogs.com/zhangmiao-chp/archive/2011/05/05/2038285.html
基於MongoDB GridFS的圖片儲存 
http://liut.cc/blog/2010/12/about-imsto_my-first-open-source-project.html
nginx+mongodb-gridfs+squid
http://1008305.blog.51cto.com/998305/885340

HDFS命令

檢視使用幫助 輸入hadoop ,回車會有幫助提示。提示中有jar、fs等。操作檔案系統明顯是使用hadoop fs 再回車 就會出現hadoop fs 的引數幫助了:
Usage: hadoop fs [generic options]
        [-appendToFile <localsrc> ... <dst>]  將檔案追加到指定檔案
        [-cat [-ignoreCrc] <src> ...]     檢視檔案內容
        [-checksum <src> ...]     獲取校驗格 很少用
        [-chgrp [-R] GROUP PATH...]   改變HDFS中檔案的所屬組
        [-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]  改變檔案許可權
        [-chown [-R] [OWNER][:[GROUP]] PATH...]   改變檔案的所屬使用者或所屬組
        [-copyFromLocal [-f] [-p] [-l] <localsrc> ... <dst>]   將本地系統的檔案拷貝到HDFS系統上 相當於put命令
        [-copyToLocal [-p] [-ignoreCrc] [-crc] <src> ... <localdst>] 將HDFS上的檔案拷貝到本地系統 相當於get命令
        [-count [-q] [-h] <path> ...]    統計HDFS指定路徑下檔案和資料夾的數量 以及佔用空間的大小
        [-cp [-f] [-p | -p[topax]] <src> ... <dst>]  將HDFS的檔案拷貝到另一個地方 指的將HDFS檔案拷貝到HDFS的另一處
        [-createSnapshot <snapshotDir> [<snapshotName>]]
        [-deleteSnapshot <snapshotDir> <snapshotName>]
        [-df [-h] [<path> ...]]
        [-du [-s] [-h] <path> ...]
        [-expunge]
        [-find <path> ... <expression> ...]
        [-get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]  下載檔案到本地系統
        [-getfacl [-R] <path>]
        [-getfattr [-R] {-n name | -d} [-e en] <path>]
        [-getmerge [-nl] <src> <localdst>]
        [-help [cmd ...]]   -help+命令  檢視命令的使用幫助
        [-ls [-d] [-h] [-R] [<path> ...]]    HDFS檔案檢視  引數-R 表示遞迴檢視  -H  將檔案大小由位元組單位轉為K M等單位
        [-mkdir [-p] <path> ...]  在HDFS系統上建立資料夾
        [-moveFromLocal <localsrc> ... <dst>] 從本地剪下一個檔案上傳到HDFS系統(相當於上傳檔案後將本地此檔案刪除)
        [-moveToLocal <src> <localdst>] 相當於將HDFS上的檔案下載到本地 然後再將HDFS系統上的此檔案刪除
        [-mv <src> ... <dst>]   將HDFS系統的內容從一處移動到HDFS系統上的另一位置
        [-put [-f] [-p] [-l] <localsrc> ... <dst>]  將本地檔案上傳至HDFS系統
        [-renameSnapshot <snapshotDir> <oldName> <newName>]
        [-rm [-f] [-r|-R] [-skipTrash] <src> ...] 將HDFS系統上的某檔案刪除(不加引數只能刪除檔案型別 加-R可刪除資料夾)
        [-rmdir [--ignore-fail-on-non-empty] <dir> ...] 只能刪除HDFS上的資料夾 有很大侷限性
        [-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
        [-setfattr {-n name [-v value] | -x name} <path>]
        [-setrep [-R] [-w] <rep> <path> ...]
        [-stat [format] <path> ...]
        [-tail [-f] <file>]   檢視檔案尾部的內容
        [-test -[defsz] <path>]
        [-text [-ignoreCrc] <src> ...] 相當於-cat 檢視HDFS系統上的檔案內容
        [-touchz <path> ...]  可以在HDFS系統上建立檔案 相當於Linux系統的touch命令
        [-truncate [-w] <length> <path> ...]
        [-usage [cmd ...]]
各個引數的使用方法如下:hadoop fs [<options> ...](1) cat
使用方法:hadoop fs -cat URI [URI …]
將路徑指定檔案的內容輸出到stdout 。
示例:
hadoop fs -cat hdfs://host1:port1/file1 hdfs://host2:port2/file2
hadoop fs -cat file:///file3 /user/hadoop/file4
返回值:
成功返回0,失敗返回-1。
(2) chgrp
使用方法:hadoop fs -chgrp [-R] GROUP URI [URI …] Change group association of files. With -R , make the change recursively through the directory structure. The user must be the owner of files, or else a super-user. Additional information is in the Permissions User Guide . -->
改變檔案所屬的組。使用-R 將使改變在目錄結構下遞迴進行。命令的使用者必須是 檔案的所有者或者超級使用者。更多的資訊請參見HDFS 許可權使用者指南 。
(3) chmod
使用方法:hadoop fs -chmod [-R] <MODE[,MODE]... | OCTALMODE> URI [URI …]
改變檔案的許可權。使用-R 將使改變在目錄結構下遞迴進行。命令的使用者必須是文 件的所有者或者超級使用者。更多的資訊請參見HDFS 許可權使用者指南 。
(4) chown
使用方法:hadoop fs -chown [-R] [OWNER][:[GROUP]] URI [URI ]
改變檔案的擁有者。使用-R 將使改變在目錄結構下遞迴進行。命令的使用者必須是 超級使用者。更多的資訊請參見HDFS 許可權使用者指南 。
(5) copyFromLocal
使用方法:hadoop fs -copyFromLocal <localsrc> URI
除了限定源路徑是一個本地檔案外,和put 命 令相似。
(6) copyToLocal
使用方法:hadoop fs -copyToLocal [-ignorecrc] [-crc] URI <localdst>
除了限定目標路徑是一個本地檔案外,和get 命 令類似。
(7) cp
使用方法:hadoop fs -cp URI [URI …] <dest>
將檔案從源路徑複製到目標路徑。這個命令允許有多個源路徑,此時目標路徑必須是一個目錄。
示例:
hadoop fs -cp /user/hadoop/file1 /user/hadoop/file2
hadoop fs -cp /user/hadoop/file1 /user/hadoop/file2 /user/hadoop/dir
返回值:
成功返回0,失敗返回-1。注意:cp命令限定輸入和輸出地址都必須是HDFS上的地址
(8) du
使用方法:hadoop fs -du URI [URI …]
顯示目錄中所有檔案的大小,或者當只指定一個檔案時,顯示此檔案的大小。
示例:
hadoop fs -du /user/hadoop/dir1 /user/hadoop/file1 hdfs://host:port/user/hadoop/dir1
返回值:
成功返回0,失敗返回-1。
(9) dus
使用方法:hadoop fs -dus <args>
顯示檔案的大小。
(10)  expunge
使用方法:hadoop fs -expunge
清空回收站。請參考HDFS 設計 文件以獲取更多關於回收站特性的資訊。
(11) get
使用方法:hadoop fs -get [-ignorecrc] [-crc] <src> <localdst>
複製檔案到本地檔案系統。可用-ignorecrc 選項複製CRC校驗失敗的文 件。使用-crc 選項複製檔案以及CRC資訊。
示例:
hadoop fs -get /user/hadoop/file localfile
hadoop fs -get hdfs://host:port/user/hadoop/file localfile
返回值:
成功返回0,失敗返回-1。
(12) getmerge
使用方法:hadoop fs -getmerge <src> <localdst> [addnl]
接受一個源目錄和一個目標檔案作為輸入,並且將源目錄中所有的檔案連線成本地目標檔案。addnl 是 可選的,用於指定在每個檔案結尾新增一個換行符。
(13) ls
使用方法:hadoop fs -ls <args>
如果是檔案,則按照如下格式返回檔案資訊:
檔名 <副本數> 檔案大小 修改日期 修改時間 許可權 使用者ID 組ID
如果是目錄,則返回它直接子檔案的一個列表,就像在Unix中一樣。目錄返回列表的資訊如下:
目錄名 <dir> 修改日期 修改時間 許可權 使用者ID 組ID
示例:
hadoop fs -ls /user/hadoop/file1 /user/hadoop/file2 hdfs://host:port/user/hadoop/dir1 /nonexistentfile
返回值:
成功返回0,失敗返回-1。
(14) lsr
使用方法:hadoop fs -lsr <args>
ls 命令的遞迴版本。類似於Unix中的ls -R 。
(15) mkdir
使用方法:hadoop fs -mkdir <paths>
接受路徑指定的uri作為引數,建立這些目錄。其行為類似於Unix的mkdir -p,它會建立路徑中的各級父目錄。
示例:
hadoop fs -mkdir /user/hadoop/dir1 /user/hadoop/dir2
hadoop fs -mkdir hdfs://host1:port1/user/hadoop/dir hdfs://host2:port2/user/hadoop/dir
返回值:
成功返回0,失敗返回-1。
(16) movefromLocal
使用方法:dfs -moveFromLocal <src> <dst>
輸出一個”not implemented“資訊。
(17) mv
使用方法:hadoop fs -mv URI [URI …] <dest>
將檔案從源路徑移動到目標路徑。這個命令允許有多個源路徑,此時目標路徑必須是一個目錄。不允許在不同的檔案系統間移動檔案。
示例:
hadoop fs -mv /user/hadoop/file1 /user/hadoop/file2
hadoop fs -mv hdfs://host:port/file1 hdfs://host:port/file2 hdfs://host:port/file3 hdfs://host:port/dir1
返回值:
成功返回0,失敗返回-1。
(18) put
使用方法:hadoop fs -put <localsrc> ... <dst>
從本地檔案系統中複製單個或多個源路徑到目標檔案系統。也支援從標準輸入中讀取輸入寫入目標檔案系統。
hadoop fs -put localfile /user/hadoop/hadoopfile
hadoop fs -put localfile1 localfile2 /user/hadoop/hadoopdir
hadoop fs -put localfile hdfs://host:port/hadoop/hadoopfile
hadoop fs -put - hdfs://host:port/hadoop/hadoopfile
從標準輸入中讀取輸入。
返回值:
成功返回0,失敗返回-1。
(19) rm
使用方法:hadoop fs -rm URI [URI …]
刪除指定的檔案。只刪除非空目錄和檔案。請參考rmr命令瞭解遞迴刪除。
示例:
hadoop fs -rm hdfs://host:port/file /user/hadoop/emptydir
返回值:
成功返回0,失敗返回-1。
(20) rmr
使用方法:hadoop fs -rmr URI [URI …]
delete的遞迴版本。
示例:
hadoop fs -rmr /user/hadoop/dir
hadoop fs -rmr hdfs://host:port/user/hadoop/dir
返回值:
成功返回0,失敗返回-1。
(21) setrep
使用方法:hadoop fs -setrep [-R] <path>
改變一個檔案的副本系數。-R選項用於遞迴改變目錄下所有檔案的副本系數。
示例:
hadoop fs -setrep -w 3 -R /user/hadoop/dir1
返回值:
成功返回0,失敗返回-1。
(22) stat
使用方法:hadoop fs -stat URI [URI …]
返回指定路徑的統計資訊。
示例:
hadoop fs -stat path
返回值:
成功返回0,失敗返回-1。
(23) tail
使用方法:hadoop fs -tail [-f] URI
將檔案尾部1K位元組的內容輸出到stdout。支援-f選項,行為和Unix中一致。
示例:
hadoop fs -tail pathname
返回值:
成功返回0,失敗返回-1。
(24) test
使用方法:hadoop fs -test -[ezd] URI
選項:
-e 檢查檔案是否存在。如果存在則返回0。
-z 檢查檔案是否是0位元組。如果是則返回0。
-d 如果路徑是個目錄,則返回1,否則返回0。
示例:
hadoop fs -test -e filename
(25) text
使用方法:hadoop fs -text <src>
將原始檔輸出為文字格式。允許的格式是zip和TextRecordInputStream。
(26) touchz
使用方法:hadoop fs -touchz URI [URI …]
建立一個0位元組的空檔案。
示例:
hadoop -touchz pathname

HDFS 架構


NameNode:負責管理節點DataNode:負責儲存資料Secondary NameNode:並不是NameNode的熱備,只是NameNode的助理。Metadata元資料也稱為schema互動過程:1. 首先客戶端進行一些Metadata options與NameNode進行互動,NameNode會查詢元資料資訊。2. NameNode從記憶體中查詢元資料資訊(為了快速和安全 NameNode會將元資料資訊儲存在記憶體中,並序列化一份到磁碟)3. NameNode會將查詢到的元資料資訊返回給Client。4. Client會根據元資料資訊依照機架感知(就近原則)依次從DataNodes中獲取資料(一個NameNode可以儲存多個Block,例如客戶端請求一個512M的資料  ,而這個資料可能在其中的一臺機器上儲存了三個Block,在另一個DataNode上儲存了一個Block,client就會優先選擇離自己最近 走交換機次數最少的DataNode進行獲取 然再從另一個DataNode中獲取)5. 為了資料安全NameNode會讓DataNode執行一些Block operations 按照配置的副本集數量對DataNode中相應的資料進行水平復制

NameNode中Metadata內容


/test/a.log  表示某檔案在HDFS上的儲存地址3                表示此檔案儲存的副本集數量{blk_1,blk_2}          表示此檔案被分成了哪幾個塊
{blk_1:[h0,h1,h3]}   表示塊blk_1副本集分別在DataNode為h0、h1、h3上各存放了一份
{blk_2:[h0,h2,h4]}   表示塊blk_1副本集分別在DataNode為h0、h1、h3上各存放了一份
示例如下:假設一個客戶端向NameNode傳送請求獲取/test/a.log  NameNode就會將此檔案的metadata資料資訊傳送給這個客戶端,這個客戶端就會根據元資料資訊獲取塊資料,在獲取塊blk_1時 根據就近原則從h0、h1、h3中選擇離自己最近的DataNode獲取blk_1,在獲取blk_1後,再獲取blk_2塊資料,仍然從h0,h2,h4中選擇離自己最近的DataNode獲取blk_2 假設h2離客戶端最近(機架感應機制 根據請求通過交換機的數量等資訊進行判定) 那麼優先從h2中獲取blk_2, 如果h2出現了問題和錯誤,那麼再從h0和h4中獲取。
注意:是根據校驗核機制斷定DataNode上的檔案是否出現錯誤,每一個檔案(塊)都有一個對應的校驗值,一旦檔案損壞 其校驗值就會改變,如果校驗值與元資料中對應的校驗值不一致就會認為該檔案已經損壞。校驗核機制常見的有MD5和CRC32校驗核機制,因為MD5校驗核機制比較慢,HDFS使用的是CRC32校驗機制。

HDFS主要物件概述

NameNodeNameNode是整個檔案系統的管理節點,它維護著整個檔案系統的檔案目錄樹,檔案/目錄的元資訊和每個檔案對應的資料塊列表。接收使用者的請求。檔案包括:存放地址由在hdfs-site.xml中的dfs.name.dir屬性指定① fismage:元資料的映象檔案。儲存某一時段NameNode記憶體的元資料資訊。② edits:操作日誌檔案。③ fstime:儲存最近一次checkpoint的時間以上這些檔案是儲存在Linux檔案系統之上的。NameNode的工作特點:① NameNode始終在記憶體中儲存metadata,用於處理"讀請求"。② 如果有"寫請求"到來時,NameNode首先會寫editlog到磁碟,即向edits檔案中寫日誌,成功返回後,才會修改記憶體,並向客戶端返回。③ Hadoop會維護一個fsimage檔案,也就是NameNode中metadata的映象,但是fsimage不會隨時與NameNode記憶體中的metadata保持一致,而是每隔一段時間通過合併edits檔案來更新內容。Secondary NameNode就是用來合併fsimage和edits檔案來更新NameNode的metadata的。Secondary NameNodeHA(Hadoop夠可靠性)的一個解決方案,但是不支援熱備。執行過程:從NameNode上下載元資料資訊(fsimage,edits),然後將兩者合併,生成新的fsimage,在本地生成,並將其推送到NameNode,替換成fsimage。Secondary NameNode預設安裝在NameNode節點上,但是並不安全。Secondary NameNode的工作流程:① Secondary通知NameNode切換edits檔案。② Secondary從NameNode獲得fsimage和edits(通過http)。③ Secondary將fsimage載入記憶體,然後開始合併edits。④ Secondary 將新的fsimage返回給NameNode。⑤ NameNode用新的fsimage替換舊的fsimage。合併時機checkpointfs.checkpoint.period 指定兩次checkpoint的最大時間間隔,預設3600秒。 
fs.checkpoint.size    規定edits檔案的最大值,一旦超過這個值則強制checkpoint,不管是否到達最大時間間隔。預設大小是64M。
DataNode提供真實檔案資料的儲存服務檔案塊(block):最基本的儲存單位。對於檔案內容而言,一個檔案的長度大小是size,那麼從檔案的0偏移開始,按照固定的大小,順序對檔案進行劃分並編號,劃分好每一個塊稱為Block。HDFS在1.0中預設的Block大小是64MB,在2.0中預設是128MB。以一個256MB檔案為例,共有256/128=2個Block。不同於普通檔案系統的是,HDFS中,如果一個檔案小於一個數據塊的大小,並不佔用整個Block資料塊的儲存空間Replication。多副本,預設是3個。(hdfs-site.xml的dfs.replication屬性)注意:Secondary NameNode只存在於偽分散式系統中,真實叢集中是不需要Secondary NameNode的。

NameNode和Secondary協作流程


(以上是hadoop1.0的合併流程,2.0已經不再使用此種方式)第一步:client向NameNode傳送請求資訊,比如上傳檔案 NameNode檢視檔案資訊 判斷應該將此檔案分配到哪些臺DataNode上
第二步:NameNode將元資料資訊反饋給client
第三步:client根據元資料資訊向對應的DataNode裡面寫資料,客戶端寫資料的同時 NameNode會操作對應的edits檔案,不管客戶端上傳成功還是失敗 NameNode都會在對應的edits檔案中新增一行記錄(+1)  同時記憶體中的metadata資訊也會改變新增一行描述資訊,但是fsimage(序列化到磁碟metadata資訊)中沒有改變 即未同步,在滿足一定的條件下Secondary NameNode發現記憶體中的metadatas和fsimage內容不一致,開始工作從NameNode中下載獲取edits和fsimage檔案,開始合併 根據edits中的操作資訊向fsimage中寫入metadata資訊 這時fsimage資訊和記憶體中的metadatas資訊一致 即完成同步 同時刪除NameNode上的edits檔案, 將新生成的edits.new替換為edits。一旦Secondary NameNode合併edits和fsimage檔案 就會完成記憶體中metadata和fsimage中元資料資訊的同步。
合併的時機:
fs.checkpoint.period 按照一定的時間間隔,預設是3600秒 即一個小時。
fs.checkpoint.size  當edits檔案大小達到規定的最大值,就會觸發合併條件。
注意:注意在開始合併的過程中處於資料安全的考慮(例如Secondary NameNode在合併時下載檔案失敗或者合併失敗等各種意外),在合併的過程中不會刪除NameNode上的edits和fsimage檔案 為了確保資料的一致性,在合併時會生成一個新的edits檔案即edits.new 。在合併的過程中如果接收到檔案操作的請求(例如檔案上傳等) 會使用這個新的edits檔案(edits.new)來存放操作檔案的資訊。在Secondary NameNode合併完成併成功向NameNode傳送新的fsimage檔案後NameNode才會將edits.new替換掉舊的edits檔案,將新的fsimage替換舊的fsimage

心跳機制:所謂的心跳機制就是每隔一段時間心跳一次。就是DataNode每隔一段時間會向DataNode彙報資訊,如果某個塊出現了問題 NameNode通過彙報的資訊檢測到某個塊的副本集的數量小於配置的數量,就會通知其它儲存此Block副本集的DataNode向別的機器複製Block。如果DataNode很長時間沒有彙報,NameNode就會認為此DataNode宕機。

RPC(Remote Procedure Call)

RPC——遠端過程呼叫協議,它是一種通過網路從遠端計算機程式上請求服務,而不需要了解底層網路技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,為通訊程式之間攜帶資訊資料。在OSI網路通訊模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網路分散式多程式在內的應用程式更加容易。一句話概括就是不同程序之間的方法呼叫。
RPC採用客戶機/伺服器模式。請求程式就是一個客戶機,而服務提供程式就是一個伺服器。首先,客戶機呼叫程序傳送一個有程序引數的呼叫資訊到服務程序,然後等待應答資訊。在伺服器端,程序保持睡眠狀態直到呼叫資訊的到達為止。當一個呼叫資訊到達,伺服器獲得程序引數,計算結果,傳送答覆資訊,然後等待下一個呼叫資訊,最後,客戶端呼叫程序接收答覆資訊,獲得程序結果,然後呼叫執行繼續進行。
hadoop的整個體系結構就是構建在RPC之上的(見org.apache.hadoop.ipc)。RPC的底層也是socket實現的,RPC的實現原理就是定義一個協議介面,協議介面中有唯一的versionID以及伺服器端要執行的方法,由伺服器端程式類實現此介面,客戶端程式使用動態代理對此介面進行增強便可獲得伺服器端的代理物件,利用代理物件呼叫協議介面中的方法,從而和伺服器端進行通訊。示例如下:(1)自定義一個介面Bizable(由Server端進行實現)
package liuxun.test.hadoop.rpc;

public interface Bizable {
	public static final long versionID = 1000000L;
	public  String sayHello(String name);
}
(2) 定義Server端RPCServer,並實現此介面
package liuxun.test.hadoop.rpc;

import java.io.IOException;

import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.ipc.RPC.Server;

public class RPCServer implements Bizable {
	public String sayHello(String name) {
		return "hello " + name;
	}

	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		Server server = new RPC.Builder(conf).setProtocol(Bizable.class).setInstance(new RPCServer()).setBindAddress("192.168.0.3")
				.setPort(9988).build();
		server.start();
	}
}
(3) RPCClient(RPC客戶端)
package liuxun.test.hadoop.rpc;

import java.io.IOException;
import java.net.InetSocketAddress;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;

public class RPCClient {
	public static void main(String[] args) throws Exception {
		Bizable proxy = RPC.getProxy(Bizable.class, 1112222L, new InetSocketAddress("192.168.0.3", 9988),
				new Configuration());
		String result = proxy.sayHello("hadoop"); //$NON-NLS-1$
		System.out.println(result);
		RPC.stopProxy(proxy);
	}
}
測試:然後將RPCServer程式打包,指定Main方法,上傳至hadoop0。使用java -jar  <RPCSer.jar>在Eclipse上執行RPCClient程式,便可將伺服器端返回的內容列印。或者將RPCClient程式打包上傳至Linux,在Eclipse上執行RPCServer程式,開啟監聽,在Linux上執行RPCClient的jar包,便可將伺服器返回的內容在Linux終端列印。

Java程式操作HDFS

程式上傳時容易出現的異常
rg.apache.hadoop.security.AccessControlException: Permission denied: user=liuxun, access=WRITE, inode="/":root:supergroup:drwxr-xr-x
解決方案:偽裝成root
fs = FileSystem.get(new URI("hdfs://hadoop0:9000"), new Configuration(),"root"); 
package liuxun.test.hadoop.hdfs;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.junit.Before;
import org.junit.Test;

public class HDFSDemo {
	FileSystem fs  = null;
	@Before
	public void init() throws Exception {
		// 首先建立FileSystem的實現類(工具類)
		fs = FileSystem.get(new URI("hdfs://hadoop0:9000"), new Configuration(),"root"); 
	}
	
	@Test
	public void testUpload() throws  Exception {
		// 讀取本地系統的檔案,返回輸入流
		InputStream in = new FileInputStream("/Users/liuxun/Desktop/Eclipse.zip");
		// 在HDFS上建立一個檔案,返回輸出流
		OutputStream out  = fs.create(new Path("/testEclipse"));
		// 輸入——>輸出
		IOUtils.copyBytes(in, out, 4096, true);
	}
	
	@Test
	public void testDownload() throws Exception { //最簡潔的下載方式
		fs.copyToLocalFile(new Path("/log.log"), new Path("/Users/liuxun/Downloads/log.txt"));
	}
	@Test
	public void testMkdir() throws Exception {
		boolean flag = fs.mkdirs(new Path("/aaa/bbb/ccc"));
		System.out.println(flag);
	}
	
	@Test
	public void testDelete() throws Exception {
		// 第二個引數recursive boolean 表示是否遞迴刪除所有子檔案/資料夾
		boolean flag = fs.delete(new Path("/aaa/bbb"), true); 
		System.out.println(flag);
	}
	
	
	public static void main(String[] args) throws Exception {
		FileSystem fs = FileSystem.get(new URI("hdfs://hadoop0:9000"), new Configuration()); 
		InputStream in = fs.open(new Path("/words"));
		OutputStream out = new FileOutputStream("/Users/liuxun/Downloads/t.txt");
		IOUtils.copyBytes(in, out , 4096,true);
		
	}
}

斷點除錯分析HDFS

除錯技巧Eclipse斷點快捷鍵:(command+t 檢視當前類的繼承關係  command+o檢視當前類中的方法 shift+command+t 查詢類)放行到下一個斷點F8  跳進方法F5  單行執行F6 跳出方法F7

在斷點除錯時,如果一個方法有多個方法的返回值作為引數,需要多次F5跳進去,F7跳出來,反覆執行方可進入到主方法。原始碼分析總結:FileSystem.get --> 通過反射例項化了一個DistributedFileSystem --> new DFSCilent()把他作為自己的成員變數
在DFSClient構造方法裡面,呼叫了createNamenode,使用了RPC機制,得到了一個NameNode的代理物件,就可以和NameNode進行通訊了
FileSystem --> DistributedFileSystem --> DFSClient --> NameNode的代理
上傳檔案時的順序圖如下:

HDFS寫過程


1.初始化FileSystem,客戶端呼叫create()來建立檔案
2.FileSystem用RPC呼叫元資料節點,在檔案系統的名稱空間中建立一個新的檔案,元資料節點首先確定檔案原來不存在,並且客戶端有建立檔案的許可權,然後建立新檔案。
3.FileSystem返回DFSOutputStream,客戶端用於寫資料,客戶端開始寫入資料。
4.DFSOutputStream將資料分成塊,寫入data queue。data queue由Data Streamer讀取,並通知元資料節點分配資料節點,用來儲存資料塊(每塊預設複製3塊)。分配的資料節點放在一個pipeline裡。Data Streamer將資料塊寫入pipeline中的第一個資料節點。第一個資料節點將資料塊傳送給第二個資料節點。第二個資料節點將資料傳送給第三個資料節點。
5.DFSOutputStream為發出去的資料塊儲存了ack queue,等待pipeline中的資料節點告知資料已經寫入成功。
6.當客戶端結束寫入資料,則呼叫stream的close函式。此操作將所有的資料塊寫入pipeline中的資料節點,並等待ack queue返回成功。最後通知元資料節點寫入完畢。
7.如果資料節點在寫入的過程中失敗,關閉pipeline,將ack queue中的資料塊放入data queue的開始,當前的資料塊在已經寫入的資料節點中被元資料節點賦予新的標示,則錯誤節點重啟後能夠察覺其資料塊是過時的,會被刪除。失敗的資料節點從pipeline中移除,另外的資料塊則寫入pipeline中的另外兩個資料節點。元資料節點則被通知此資料塊是複製塊數不足,將來會再建立第三份備份。

HDFS讀過程


1.初始化FileSystem,然後客戶端(client)用FileSystem的open()函式開啟檔案
2.FileSystem用RPC呼叫元資料節點,得到檔案的資料塊資訊,對於每一個數據塊,元資料節點返回儲存資料塊的資料節點的地址。
3.FileSystem返回FSDataInputStream給客戶端,用來讀取資料,客戶端呼叫stream的read()函式開始讀取資料。
4.DFSInputStream連線儲存此檔案第一個資料塊的最近的資料節點,data從資料節點讀到客戶端(client)
5.當此資料塊讀取完畢時,DFSInputStream關閉和此資料節點的連線,然後連線此檔案下一個資料塊的最近的資料節點。
6.當客戶端讀取完畢資料的時候,呼叫FSDataInputStream的close函式。
7.在讀取資料的過程中,如果客戶端在與資料節點通訊出現錯誤,則嘗試連線包含此資料塊的下一個資料節點。
8.失敗的資料節點將被記錄,以後不再連線。