HDFS(Hadoop Distributed File System)是Hadoop項目的核心子項目,是分佈式計算中數據存儲管理的基礎,是基於流數據模式訪問和處理超大文件的需求而開發的,能夠運行於廉價的商用服務器上。它所具備的高容錯、高可靠性、高可擴展性、高得到性、高吞吐率等特徵爲海量數據提供了不怕故障的存儲,爲超大數據集(Large Data Set)的應用處理帶來了不少便利。java
Hadoop整合了衆多文件系統,在其中有一個綜合性的文件系統抽象,它提供了文件系統實現的各種接口,HDFS只是這個抽象文件系統的一個實例。提供了一個高層的文件系統抽象類org.apache.hadoop.fs.FileSystem,這個抽象類展現了一個分佈式文件系統,並有幾個具體實現,以下表1-1所示。node
表1-1 Hadoop的文件系統apache
文件系統瀏覽器 |
URI方案緩存 |
Java實現安全 (org.apache.hadoop)服務器 |
定義網絡 |
Local數據結構 |
file架構 |
fs.LocalFileSystem |
支持有客戶端校驗和本地文件系統。帶有校驗和的本地系統文件在fs.RawLocalFileSystem中實現。 |
HDFS |
hdfs |
hdfs.DistributionFileSystem |
Hadoop的分佈式文件系統。 |
HFTP |
hftp |
hdfs.HftpFileSystem |
支持經過HTTP方式以只讀的方式訪問HDFS,distcp常常用在不一樣的HDFS集羣間複製數據。 |
HSFTP |
hsftp |
hdfs.HsftpFileSystem |
支持經過HTTPS方式以只讀的方式訪問HDFS。 |
HAR |
har |
fs.HarFileSystem |
構建在Hadoop文件系統之上,對文件進行歸檔。Hadoop歸檔文件主要用來減小NameNode的內存使用。 |
KFS |
kfs |
fs.kfs.KosmosFileSystem |
Cloudstore(其前身是Kosmos文件系統)文件系統是相似於HDFS和Google的GFS文件系統,使用C++編寫。 |
FTP |
ftp |
fs.ftp.FtpFileSystem |
由FTP服務器支持的文件系統。 |
S3(本地) |
s3n |
fs.s3native.NativeS3FileSystem |
基於Amazon S3的文件系統。 |
S3(基於塊) |
s3 |
fs.s3.NativeS3FileSystem |
基於Amazon S3的文件系統,以塊格式存儲解決了S3的5GB文件大小的限制。 |
Hadoop提供了許多文件系統的接口,用戶可使用URI方案選取合適的文件系統來實現交互。
HDFS(Hadoop Distributed File System)默認的最基本的存儲單位是64M的數據塊。
和普通文件系統相同的是,HDFS中的文件是被分紅64M一塊的數據塊存儲的。
不一樣於普通文件系統的是,HDFS中,若是一個文件小於一個數據塊的大小,並不佔用整個數據塊存儲空間。
HDFS體系結構中有兩類節點,一類是NameNode,又叫"元數據節點";另外一類是DataNode,又叫"數據節點"。這兩類節點分別承擔Master和Worker具體任務的執行節點。
1)元數據節點用來管理文件系統的命名空間
其將全部的文件和文件夾的元數據保存在一個文件系統樹中。
這些信息也會在硬盤上保存成如下文件:命名空間鏡像(namespace image)及修改日誌(edit log)
其還保存了一個文件包括哪些數據塊,分佈在哪些數據節點上。然而這些信息並不存儲在硬盤上,而是在系統啓動的時候從數據節點收集而成的。
2)數據節點是文件系統中真正存儲數據的地方。
客戶端(client)或者元數據信息(namenode)能夠向數據節點請求寫入或者讀出數據塊。
其週期性的向元數據節點回報其存儲的數據塊信息。
3)從元數據節點(secondary namenode)
從元數據節點並非元數據節點出現問題時候的備用節點,它和元數據節點負責不一樣的事情。
其主要功能就是週期性將元數據節點的命名空間鏡像文件和修改日誌合併,以防日誌文件過大。這點在下面會相信敘述。
合併事後的命名空間鏡像文件也在從元數據節點保存了一份,以防元數據節點失敗的時候,能夠恢復。
VERSION文件是java properties文件,保存了HDFS的版本號。
layoutVersion是一個負整數,保存了HDFS的持續化在硬盤上的數據結構的格式版本號。
namespaceID是文件系統的惟一標識符,是在文件系統初次格式化時生成的。
cTime此處爲0
storageType表示此文件夾中保存的是元數據節點的數據結構。
namespaceID=1232737062
cTime=0
storageType=NAME_NODE
layoutVersion=-18
數據節點的VERSION文件格式以下:
namespaceID=1232737062
storageID=DS-1640411682-127.0.1.1-50010-1254997319480
cTime=0
storageType=DATA_NODE
layoutVersion=-18
blk_<id>保存的是HDFS的數據塊,其中保存了具體的二進制數據。
blk_<id>.meta保存的是數據塊的屬性信息:版本信息,類型信息,和checksum
當一個目錄中的數據塊到達必定數量的時候,則建立子文件夾來保存數據塊及數據塊屬性信息。
當文件系統客戶端(client)進行寫操做時,首先把它記錄在修改日誌中(edit log)
元數據節點在內存中保存了文件系統的元數據信息。在記錄了修改日誌後,元數據節點則修改內存中的數據結構。
每次的寫操做成功以前,修改日誌都會同步(sync)到文件系統。
fsimage文件,也即命名空間映像文件,是內存中的元數據在硬盤上的checkpoint,它是一種序列化的格式,並不可以在硬盤上直接修改。
同數據的機制類似,當元數據節點失敗時,則最新checkpoint的元數據信息從fsimage加載到內存中,而後逐一從新執行修改日誌中的操做。
從元數據節點就是用來幫助元數據節點將內存中的元數據信息checkpoint到硬盤上的
checkpoint的過程以下:
從元數據節點通知元數據節點生成新的日誌文件,之後的日誌都寫到新的日誌文件中。
從元數據節點用http get從元數據節點得到fsimage文件及舊的日誌文件。
從元數據節點將fsimage文件加載到內存中,並執行日誌文件中的操做,而後生成新的fsimage文件。
從元數據節點獎新的fsimage文件用http post傳回元數據節點
元數據節點能夠將舊的fsimage文件及舊的日誌文件,換爲新的fsimage文件和新的日誌文件(第一步生成的),而後更新fstime文件,寫入這次checkpoint的時間。
這樣元數據節點中的fsimage文件保存了最新的checkpoint的元數據信息,日誌文件也從新開始,不會變的很大了。
HDFS是一個主/從(Mater/Slave)體系結構,從最終用戶的角度來看,它就像傳統的文件系統同樣,能夠經過目錄路徑對文件執行CRUD(Create、Read、Update和Delete)操做。但因爲分佈式存儲的性質,HDFS集羣擁有一個NameNode和一些DataNode。NameNode管理文件系統的元數據,DataNode存儲實際的數據。客戶端經過同NameNode和DataNodes的交互訪問文件系統。客戶端聯繫NameNode以獲取文件的元數據,而真正的文件I/O操做是直接和DataNode進行交互的。
圖3.1 HDFS整體結構示意圖
1)NameNode、DataNode和Client
NameNode能夠看做是分佈式文件系統中的管理者,主要負責管理文件系統的命名空間、集羣配置信息和存儲塊的複製等。NameNode會將文件系統的Meta-data存儲在內存中,這些信息主要包括了文件信息、每個文件對應的文件塊的信息和每個文件塊在DataNode的信息等。
DataNode是文件存儲的基本單元,它將Block存儲在本地文件系統中,保存了Block的Meta-data,同時週期性地將全部存在的Block信息發送給NameNode。
Client就是須要獲取分佈式文件系統文件的應用程序。
2)文件寫入
Client向NameNode發起文件寫入的請求。
NameNode根據文件大小和文件塊配置狀況,返回給Client它所管理部分DataNode的信息。
Client將文件劃分爲多個Block,根據DataNode的地址信息,按順序寫入到每個DataNode塊中。
3)文件讀取
Client向NameNode發起文件讀取的請求。
NameNode返回文件存儲的DataNode的信息。
Client讀取文件信息。
HDFS典型的部署是在一個專門的機器上運行NameNode,集羣中的其餘機器各運行一個DataNode;也能夠在運行NameNode的機器上同時運行DataNode,或者一臺機器上運行多個DataNode。一個集羣只有一個NameNode的設計大大簡化了系統架構。
1)處理超大文件
這裏的超大文件一般是指百MB、設置數百TB大小的文件。目前在實際應用中,HDFS已經能用來存儲管理PB級的數據了。
2)流式的訪問數據
HDFS的設計創建在更多地響應"一次寫入、屢次讀寫"任務的基礎上。這意味着一個數據集一旦由數據源生成,就會被複製分發到不一樣的存儲節點中,而後響應各類各樣的數據分析任務請求。在多數狀況下,分析任務都會涉及數據集中的大部分數據,也就是說,對HDFS來講,請求讀取整個數據集要比讀取一條記錄更加高效。
3)運行於廉價的商用機器集羣上
Hadoop設計對硬件需求比較低,只須運行在低廉的商用硬件集羣上,而無需昂貴的高可用性機器上。廉價的商用機也就意味着大型集羣中出現節點故障狀況的機率很是高。這就要求設計HDFS時要充分考慮數據的可靠性,安全性及高可用性。
1)不適合低延遲數據訪問
若是要處理一些用戶要求時間比較短的低延遲應用請求,則HDFS不適合。HDFS是爲了處理大型數據集分析任務的,主要是爲達到高的數據吞吐量而設計的,這就可能要求以高延遲做爲代價。
改進策略:對於那些有低延時要求的應用程序,HBase是一個更好的選擇。經過上層數據管理項目來儘量地彌補這個不足。在性能上有了很大的提高,它的口號就是goes real time。使用緩存或多master設計能夠下降client的數據請求壓力,以減小延時。還有就是對HDFS系統內部的修改,這就得權衡大吞吐量與低延時了,HDFS不是萬能的銀彈。
2)沒法高效存儲大量小文件
由於Namenode把文件系統的元數據放置在內存中,因此文件系統所能容納的文件數目是由Namenode的內存大小來決定。通常來講,每個文件、文件夾和Block須要佔據150字節左右的空間,因此,若是你有100萬個文件,每個佔據一個Block,你就至少須要300MB內存。當前來講,數百萬的文件仍是可行的,當擴展到數十億時,對於當前的硬件水平來講就無法實現了。還有一個問題就是,由於Map task的數量是由splits來決定的,因此用MR處理大量的小文件時,就會產生過多的Maptask,線程管理開銷將會增長做業時間。舉個例子,處理10000M的文件,若每一個split爲1M,那就會有10000個Maptasks,會有很大的線程開銷;若每一個split爲100M,則只有100個Maptasks,每一個Maptask將會有更多的事情作,而線程的管理開銷也將減少不少。
改進策略:要想讓HDFS能處理好小文件,有很多方法。
利用SequenceFile、MapFile、Har等方式歸檔小文件,這個方法的原理就是把小文件歸檔起來管理,HBase就是基於此的。對於這種方法,若是想找回原來的小文件內容,那就必須得知道與歸檔文件的映射關係。
橫向擴展,一個Hadoop集羣能管理的小文件有限,那就把幾個Hadoop集羣拖在一個虛擬服務器後面,造成一個大的Hadoop集羣。google也是這麼幹過的。
多Master設計,這個做用顯而易見了。正在研發中的GFS II也要改成分佈式多Master設計,還支持Master的Failover,並且Block大小改成1M,有意要調優處理小文件啊。
附帶個Alibaba DFS的設計,也是多Master設計,它把Metadata的映射存儲和管理分開了,由多個Metadata存儲節點和一個查詢Master節點組成。
3)不支持多用戶寫入及任意修改文件
在HDFS的一個文件中只有一個寫入者,並且寫操做只能在文件末尾完成,即只能執行追加操做。目前HDFS還不支持多個用戶對同一文件的寫操做,以及在文件任意位置進行修改。
先說一下"hadoop fs 和hadoop dfs的區別",看兩本Hadoop書上各有用到,但效果同樣,求證與網絡發現下面一解釋比較中肯。
粗略的講,fs是個比較抽象的層面,在分佈式環境中,fs就是dfs,但在本地環境中,fs是local file system,這個時候dfs就不能用。
1)列出HDFS文件
此處爲你展現如何經過"-ls"命令列出HDFS下的文件:
hadoop fs -ls
執行結果如圖5-1-1所示。在這裏須要注意:在HDFS中未帶參數的"-ls"命名沒有返回任何值,它默認返回HDFS的"home"目錄下的內容。在HDFS中,沒有當前目錄這樣一個概念,也沒有cd這個命令。
圖5-1-1 列出HDFS文件
2)列出HDFS目錄下某個文檔中的文件
此處爲你展現如何經過"-ls 文件名"命令瀏覽HDFS下名爲"input"的文檔中文件:
hadoop fs –ls input
執行結果如圖5-1-2所示。
圖5-1-2 列出HDFS下名爲input的文檔下的文件
3)上傳文件到HDFS
此處爲你展現如何經過"-put 文件1 文件2"命令將"Master.Hadoop"機器下的"/home/hadoop"目錄下的file文件上傳到HDFS上並重命名爲test:
hadoop fs –put ~/file test
執行結果如圖5-1-3所示。在執行"-put"時只有兩種可能,便是執行成功和執行失敗。在上傳文件時,文件首先複製到DataNode上。只有全部的DataNode都成功接收完數據,文件上傳纔是成功的。其餘狀況(如文件上傳終端等)對HDFS來講都是作了無用功。
圖5-1-3 成功上傳file到HDFS
4)將HDFS中文件複製到本地系統中
此處爲你展現如何經過"-get 文件1 文件2"命令將HDFS中的"output"文件複製到本地系統並命名爲"getout"。
hadoop fs –get output getout
執行結果如圖5-1-4所示。
圖5-1-4 成功將HDFS中output文件複製到本地系統
備註:與"-put"命令同樣,"-get"操做既能夠操做文件,也能夠操做目錄。
5)刪除HDFS下的文檔
此處爲你展現如何經過"-rmr 文件"命令刪除HDFS下名爲"newoutput"的文檔:
hadoop fs –rmr newoutput
執行結果如圖5-1-5所示。
圖5-1-5 成功刪除HDFS下的newoutput文檔
6)查看HDFS下某個文件
此處爲你展現如何經過"-cat 文件"命令查看HDFS下input文件中內容:
hadoop fs -cat input/*
執行結果如圖5-1-6所示。
圖5-1-6 HDFS下input文件的內容
"hadoop fs"的命令遠不止這些,本小節介紹的命令已能夠在HDFS上完成大多數常規操做。對於其餘操做,能夠經過"-help commandName"命令所列出的清單來進一步學習與探索。
1)報告HDFS的基本統計狀況
此處爲你展現經過"-report"命令如何查看HDFS的基本統計信息:
hadoop dfsadmin -report
執行結果如圖5-2-1所示。
圖5-2-1 HDFS基本統計信息
2)退出安全模式
NameNode在啓動時會自動進入安全模式。安全模式是NameNode的一種狀態,在這個階段,文件系統不容許有任何修改。安全模式的目的是在系統啓動時檢查各個DataNode上數據塊的有效性,同時根據策略對數據塊進行必要的複製或刪除,當數據塊最小百分比數知足的最小副本數條件時,會自動退出安全模式。
系統顯示"Name node is in safe mode",說明系統正處於安全模式,這時只須要等待17秒便可,也能夠經過下面的命令退出安全模式:
hadoop dfsadmin –safemode enter
成功退出安全模式結果如圖5-2-2所示。
圖5-2-2 成功退出安全模式
3)進入安全模式
在必要狀況下,能夠經過如下命令把HDFS置於安全模式:
hadoop dfsadmin –safemode enter
執行結果如圖5-2-3所示。
圖5-2-3 進入HDFS安全模式
4)添加節點
可擴展性是HDFS的一個重要特性,向HDFS集羣中添加節點是很容易實現的。添加一個新的DataNode節點,首先在新加節點上安裝好Hadoop,要和NameNode使用相同的配置(能夠直接從NameNode複製),修改"/usr/hadoop/conf/master"文件,加入NameNode主機名。而後在NameNode節點上修改"/usr/hadoop/conf/slaves"文件,加入新節點主機名,再創建到新加點無密碼的SSH鏈接,運行啓動命令:
start-all.sh
5)負載均衡
HDFS的數據在各個DataNode中的分佈肯能很不均勻,尤爲是在DataNode節點出現故障或新增DataNode節點時。新增數據塊時NameNode對DataNode節點的選擇策略也有可能致使數據塊分佈的不均勻。用戶可使用命令從新平衡DataNode上的數據塊的分佈:
start-balancer.sh
執行命令前,DataNode節點上數據分佈狀況如圖5-2-4所示。
負載均衡完畢後,DataNode節點上數據的分佈狀況如圖5-2-5所示。
執行負載均衡命令如圖5-2-6所示。
Hadoop中關於文件操做類基本上所有是在"org.apache.hadoop.fs"包中,這些API可以支持的操做包含:打開文件,讀寫文件,刪除文件等。
Hadoop類庫中最終面向用戶提供的接口類是FileSystem,該類是個抽象類,只能經過來類的get方法獲得具體類。get方法存在幾個重載版本,經常使用的是這個:
static FileSystem get(Configuration conf);
該類封裝了幾乎全部的文件操做,例如mkdir,delete等。綜上基本上能夠得出操做文件的程序庫框架:
operator()
{
獲得Configuration對象
獲得FileSystem對象
進行文件操做
}
經過"FileSystem.copyFromLocalFile(Path src,Patch dst)"可將本地文件上傳到HDFS的制定位置上,其中src和dst均爲文件的完整路徑。具體事例以下:
package com.hebut.file;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class CopyFile {
public static void main(String[] args) throws Exception {
Configuration conf=new Configuration();
FileSystem hdfs=FileSystem.get(conf);
//本地文件
Path src =new Path("D:\\HebutWinOS");
//HDFS爲止
Path dst =new Path("/");
hdfs.copyFromLocalFile(src, dst);
System.out.println("Upload to"+conf.get("fs.default.name"));
FileStatus files[]=hdfs.listStatus(dst);
for(FileStatus file:files){
System.out.println(file.getPath());
}
}
}
運行結果能夠經過控制檯、項目瀏覽器和SecureCRT查看,如圖6-1-一、圖6-1-二、圖6-1-3所示。
1)控制檯結果
圖6-1-1 運行結果(1)
2)項目瀏覽器
圖6-1-2 運行結果(2)
3)SecureCRT結果
圖6-1-3 運行結果(3)
經過"FileSystem.create(Path f)"可在HDFS上建立文件,其中f爲文件的完整路徑。具體實現以下:
package com.hebut.file;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class CreateFile {
public static void main(String[] args) throws Exception {
Configuration conf=new Configuration();
FileSystem hdfs=FileSystem.get(conf);
byte[] buff="hello hadoop world!\n".getBytes();
Path dfs=new Path("/test");
FSDataOutputStream outputStream=hdfs.create(dfs);
outputStream.write(buff,0,buff.length);
}
}
運行結果如圖6-2-1和圖6-2-2所示。
1)項目瀏覽器
圖6-2-1 運行結果(1)
2)SecureCRT結果
圖6-2-2 運行結果(2)
經過"FileSystem.mkdirs(Path f)"可在HDFS上建立文件夾,其中f爲文件夾的完整路徑。具體實現以下:
package com.hebut.dir;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class CreateDir {
public static void main(String[] args) throws Exception{
Configuration conf=new Configuration();
FileSystem hdfs=FileSystem.get(conf);
Path dfs=new Path("/TestDir");
hdfs.mkdirs(dfs);
}
}
運行結果如圖6-3-1和圖6-3-2所示。
1)項目瀏覽器
圖6-3-1 運行結果(1)
2)SecureCRT結果
圖6-3-2 運行結果(2)
經過"FileSystem.rename(Path src,Path dst)"可爲指定的HDFS文件重命名,其中src和dst均爲文件的完整路徑。具體實現以下:
package com.hebut.file;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class Rename{
public static void main(String[] args) throws Exception {
Configuration conf=new Configuration();
FileSystem hdfs=FileSystem.get(conf);
Path frpaht=new Path("/test"); //舊的文件名
Path topath=new Path("/test1"); //新的文件名
boolean isRename=hdfs.rename(frpaht, topath);
String result=isRename?"成功":"失敗";
System.out.println("文件重命名結果爲:"+result);
}
}
運行結果如圖6-4-1和圖6-4-2所示。
1)項目瀏覽器
圖6-4-1 運行結果(1)
2)SecureCRT結果
圖6-4-2 運行結果(2)
經過"FileSystem.delete(Path f,Boolean recursive)"可刪除指定的HDFS文件,其中f爲須要刪除文件的完整路徑,recuresive用來肯定是否進行遞歸刪除。具體實現以下:
package com.hebut.file;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class DeleteFile {
public static void main(String[] args) throws Exception {
Configuration conf=new Configuration();
FileSystem hdfs=FileSystem.get(conf);
Path delef=new Path("/test1");
boolean isDeleted=hdfs.delete(delef,false);
//遞歸刪除
//boolean isDeleted=hdfs.delete(delef,true);
System.out.println("Delete?"+isDeleted);
}
}
運行結果如圖6-5-1和圖6-5-2所示。
1)控制檯結果
圖6-5-1 運行結果(1)
2)項目瀏覽器
圖6-5-2 運行結果(2)
同刪除文件代碼同樣,只是換成刪除目錄路徑便可,若是目錄下有文件,要進行遞歸刪除。
經過"FileSystem.exists(Path f)"可查看指定HDFS文件是否存在,其中f爲文件的完整路徑。具體實現以下:
package com.hebut.file;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class CheckFile {
public static void main(String[] args) throws Exception {
Configuration conf=new Configuration();
FileSystem hdfs=FileSystem.get(conf);
Path findf=new Path("/test1");
boolean isExists=hdfs.exists(findf);
System.out.println("Exist?"+isExists);
}
}
運行結果如圖6-7-1和圖6-7-2所示。
1)控制檯結果
圖6-7-1 運行結果(1)
2)項目瀏覽器
圖6-7-2 運行結果(2)
經過"FileSystem.getModificationTime()"可查看指定HDFS文件的修改時間。具體實現以下:
package com.hebut.file;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class GetLTime {
public static void main(String[] args) throws Exception {
Configuration conf=new Configuration();
FileSystem hdfs=FileSystem.get(conf);
Path fpath =new Path("/user/hadoop/test/file1.txt");
FileStatus fileStatus=hdfs.getFileStatus(fpath);
long modiTime=fileStatus.getModificationTime();
System.out.println("file1.txt的修改時間是"+modiTime);
}
}
運行結果如圖6-8-1所示。
圖6-8-1 控制檯結果
經過"FileStatus.getPath()"可查看指定HDFS中某個目錄下全部文件。具體實現以下:
package com.hebut.file;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class ListAllFile {
public static void main(String[] args) throws Exception {
Configuration conf=new Configuration();
FileSystem hdfs=FileSystem.get(conf);
Path listf =new Path("/user/hadoop/test");
FileStatus stats[]=hdfs.listStatus(listf);
for(int i = 0; i < stats.length; ++i)
{
System.out.println(stats[i].getPath().toString());
}
hdfs.close();
}
}
運行結果如圖6-9-1和圖6-9-2所示。
1)控制檯結果
圖6-9-1 運行結果(1)
2)項目瀏覽器
圖6-9-2 運行結果(2)
經過"FileSystem.getFileBlockLocation(FileStatus file,long start,long len)"可查找指定文件在HDFS集羣上的位置,其中file爲文件的完整路徑,start和len來標識查找文件的路徑。具體實現以下:
package com.hebut.file;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class FileLoc {
public static void main(String[] args) throws Exception {
Configuration conf=new Configuration();
FileSystem hdfs=FileSystem.get(conf);
Path fpath=new Path("/user/hadoop/cygwin");
FileStatus filestatus = hdfs.getFileStatus(fpath);
BlockLocation[] blkLocations = hdfs.getFileBlockLocations(filestatus, 0, filestatus.getLen());
int blockLen = blkLocations.length;
for(int i=0;i<blockLen;i++){
String[] hosts = blkLocations[i].getHosts();
System.out.println("block_"+i+"_location:"+hosts[0]);
}
}
}
運行結果如圖6-10-1和6.10.2所示。
1)控制檯結果
圖6-10-1 運行結果(1)
2)項目瀏覽器
圖6-10-2 運行結果(2)
經過"DatanodeInfo.getHostName()"可獲取HDFS集羣上的全部節點名稱。具體實現以下:
package com.hebut.file;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
public class GetList {
public static void main(String[] args) throws Exception {
Configuration conf=new Configuration();
FileSystem fs=FileSystem.get(conf);
DistributedFileSystem hdfs = (DistributedFileSystem)fs;
DatanodeInfo[] dataNodeStats = hdfs.getDataNodeStats();
for(int i=0;i<dataNodeStats.length;i++){
System.out.println("DataNode_"+i+"_Name:"+dataNodeStats[i].getHostName());
}
}
}
運行結果如圖6-11-1所示。
圖6-11-1 控制檯結果
文件讀取的過程以下:
1)解釋一
客戶端(client)用FileSystem的open()函數打開文件。
DistributedFileSystem用RPC調用元數據節點,獲得文件的數據塊信息。
對於每個數據塊,元數據節點返回保存數據塊的數據節點的地址。
DistributedFileSystem返回FSDataInputStream給客戶端,用來讀取數據。
客戶端調用stream的read()函數開始讀取數據。
DFSInputStream鏈接保存此文件第一個數據塊的最近的數據節點。
Data從數據節點讀到客戶端(client)。
當此數據塊讀取完畢時,DFSInputStream關閉和此數據節點的鏈接,而後鏈接此文件下一個數據塊的最近的數據節點。
當客戶端讀取完畢數據的時候,調用FSDataInputStream的close函數。
在讀取數據的過程當中,若是客戶端在與數據節點通訊出現錯誤,則嘗試鏈接包含此數據塊的下一個數據節點。
失敗的數據節點將被記錄,之後再也不鏈接。
2)解釋二
使用HDFS提供的客戶端開發庫,向遠程的Namenode發起RPC請求;
Namenode會視狀況返回文件的部分或者所有block列表,對於每一個block,Namenode都會返回有該block拷貝的datanode地址;
客戶端開發庫會選取離客戶端最接近的datanode來讀取block;
讀取完當前block的數據後,關閉與當前的datanode鏈接,併爲讀取下一個block尋找最佳的datanode;
當讀完列表的block後,且文件讀取尚未結束,客戶端開發庫會繼續向Namenode獲取下一批的block列表。
讀取完一個block都會進行checksum驗證,若是讀取datanode時出現錯誤,客戶端會通知Namenode,而後再從下一個擁有該block拷貝的datanode繼續讀。
寫入文件的過程比讀取較爲複雜:
1)解釋一
客戶端調用create()來建立文件
DistributedFileSystem用RPC調用元數據節點,在文件系統的命名空間中建立一個新的文件。
元數據節點首先肯定文件原來不存在,而且客戶端有建立文件的權限,而後建立新文件。
DistributedFileSystem返回DFSOutputStream,客戶端用於寫數據。
客戶端開始寫入數據,DFSOutputStream將數據分紅塊,寫入data queue。
Data queue由Data Streamer讀取,並通知元數據節點分配數據節點,用來存儲數據塊(每塊默認複製3塊)。分配的數據節點放在一個pipeline裏。
Data Streamer將數據塊寫入pipeline中的第一個數據節點。第一個數據節點將數據塊發送給第二個數據節點。第二個數據節點將數據發送給第三個數據節點。
DFSOutputStream爲發出去的數據塊保存了ack queue,等待pipeline中的數據節點告知數據已經寫入成功。
若是數據節點在寫入的過程當中失敗:
關閉pipeline,將ack queue中的數據塊放入data queue的開始。
當前的數據塊在已經寫入的數據節點中被元數據節點賦予新的標示,則錯誤節點重啓後可以察覺其數據塊是過期的,會被刪除。
失敗的數據節點從pipeline中移除,另外的數據塊則寫入pipeline中的另外兩個數據節點。
元數據節點則被通知此數據塊是複製塊數不足,未來會再建立第三份備份。
當客戶端結束寫入數據,則調用stream的close函數。此操做將全部的數據塊寫入pipeline中的數據節點,並等待ack queue返回成功。最後通知元數據節點寫入完畢。
2)解釋二
使用HDFS提供的客戶端開發庫,向遠程的Namenode發起RPC請求;
Namenode會檢查要建立的文件是否已經存在,建立者是否有權限進行操做,成功則會爲文件建立一個記錄,不然會讓客戶端拋出異常;
當客戶端開始寫入文件的時候,開發庫會將文件切分紅多個packets,並在內部以"data queue"的形式管理這些packets,並向Namenode申請新的blocks,獲取用來存儲replicas的合適的datanodes列表,列表的大小根據在Namenode中對replication的設置而定。
開始以pipeline(管道)的形式將packet寫入全部的replicas中。開發庫把packet以流的方式寫入第一個datanode,該datanode把該packet存儲以後,再將其傳遞給在此pipeline中的下一個datanode,直到最後一個datanode,這種寫數據的方式呈流水線的形式。
最後一個datanode成功存儲以後會返回一個ack packet,在pipeline裏傳遞至客戶端,在客戶端的開發庫內部維護着"ack queue",成功收到datanode返回的ack packet後會從"ack queue"移除相應的packet。
若是傳輸過程當中,有某個datanode出現了故障,那麼當前的pipeline會被關閉,出現故障的datanode會從當前的pipeline中移除,剩餘的block會繼續剩下的datanode中繼續以pipeline的形式傳輸,同時Namenode會分配一個新的datanode,保持replicas設定的數量。
文章下載地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.8.rar