1、Hadoop集群搭建
1.1、為什么在安裝Hadoop集群之前需要重新編譯?
由于Apache給出的Hadoop的安裝包沒有提供帶C程序訪問的接口,所以我們在使用本地庫(本地庫可以用來做壓縮,以及支持C程序等等)的時候就會出問題,需要對Hadoop源碼包進行重新編譯。
1.2、Hadoop 2.X版本的架構(gòu)模型
架構(gòu)一(單節(jié)點架構(gòu)):

文件系統(tǒng)核心模塊
這里僅僅是簡單的說明一下Hadoop2.X架構(gòu)下的各個模塊,其中模塊具體的相關(guān)功能,會在后序部分解釋。
Hadoop 2.X版本的架構(gòu)中,主要分為HDFS文件存儲系統(tǒng)模塊和Yarn資源調(diào)度系統(tǒng)模塊。其中:
1、HDSF文件存儲系統(tǒng)中核心組件:
NameNode:集群當(dāng)中的主節(jié)點,主要用于管理集群當(dāng)中的各種數(shù)據(jù)。
SecondaryNameNode:主要能用于Hadoop當(dāng)中元數(shù)據(jù)信息的輔助管理。
DataNode:集群當(dāng)中的從節(jié)點,主要用于存儲集群當(dāng)中的各種數(shù)據(jù)。
2、Yarn資源調(diào)度系統(tǒng)(也叫做數(shù)據(jù)計算核心模塊)中:
ResourceManager:接收用戶的計算請求任務(wù),并負責(zé)集群的資源分配。
NodeManager:Yarn平臺的從節(jié)點,主要用于處理ResourceManager分配的任務(wù)。
架構(gòu)二(高可用架構(gòu)):

文件系統(tǒng)核心模塊
1、HDSF文件存儲系統(tǒng)中核心組件:
NameNode:集群當(dāng)中的主節(jié)點,主要用于管理集群當(dāng)中的各種數(shù)據(jù),一般都是使用兩個,實
現(xiàn)HA高可用。
JournalNode:用于元數(shù)據(jù)信息同步的管理進程,一般都是奇數(shù)個。(這是架構(gòu)高可用新出現(xiàn)的進程)
SecondaryNameNode:(和上面單節(jié)點架構(gòu)的一樣)主要能用于Hadoop當(dāng)中元數(shù)據(jù)信息的輔助管理。
DataNode:(和上面單節(jié)點架構(gòu)的一樣)集群當(dāng)中的從節(jié)點,主要用于存儲集群當(dāng)中的各種數(shù)據(jù)。
2、Yarn資源調(diào)度系統(tǒng)(也叫做數(shù)據(jù)計算核心模塊)中:
ResourceManager:接收用戶的計算請求任務(wù),并負責(zé)集群的資源分配,高可用架構(gòu)下,會有兩個,通過Zookeeper進行主節(jié)點和備份節(jié)點之間的交換。
NodeManager:Yarn平臺的從節(jié)點,主要用于處理ResourceManager分配的任務(wù)。
1.3、Hadoop集群搭建
主要修改七個文件:
1、core-site.xml
2、hdfs-site.xml
3、hadoop-env.sh
4、mapred.site.xml
5、yarn-site.xml
6、mapred-env.sh
7、slaves
1.4、配置環(huán)境變量
export HADOOP_HOME=/export/servers/hadoop-2.7.5
export PATH=:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH
2、Hadoop 核心—HDFS
2.1、HDFS文件系統(tǒng)架構(gòu)
HDFS是一個主從體系結(jié)構(gòu),由四部分組成:HDFS Client、NameNode、DataNode以及Secondary NameNode。
1、HDFS Client:客戶端
功能:
(1)文件切分:文件上傳 HDFS 的時候,Client 將文件切分成 一個一個的Block,然后進行存
儲,一般一個Block是128M。
(2)與 NameNode 交互,獲取文件的位置信息。
(3)與 DataNode 交互,讀取或者寫入數(shù)據(jù)。
(4)Client 提供一些命令來管理 和訪問HDFS,比如啟動或者關(guān)閉HDFS。
(總之一句話,HDFS就像是一個服務(wù)端,Client是一個客戶端;用戶所有的HDFS操作都必須通過Client。)
2、NameNode
功能:
(1)管理 HDFS 的命名空間。文件系統(tǒng)命名空間
(2)管理數(shù)據(jù)塊(Block)映射信息。數(shù)據(jù)塊映射信息
(3)配置副本策略。
(4)處理客戶端讀寫請求。
3、DataNode:NameNode下達命令,DataNode執(zhí)行實際的操作。
功能:
(1)Data Node以數(shù)據(jù)塊的形式存儲HDFS文件。
(2)Data Node 響應(yīng)HDFS 客戶端讀寫請求
(3)Data Node 周期性向NameNode匯報心跳信息(10分鐘一次)
(4)Data Node 周期性向NameNode匯報數(shù)據(jù)塊信息(1小時一次)
(5)Data Node 周期性向NameNode匯報緩存數(shù)據(jù)塊信息
4、Secondary NameNode:并非 NameNode 的熱備。當(dāng)NameNode 掛掉的時候,它并不能馬上替換 NameNode 并提供服務(wù)。
功能:
(1)輔助 NameNode,分擔(dān)其工作量。
(2)定期合并 fsimage和fsedits,并推送給NameNode。
(3)在緊急情況下,可輔助恢復(fù) NameNode。恢復(fù)過程
2.2、NameNode和DataNode詳解

??????在上面解釋HDFS架構(gòu)的時候,簡單的說明了NameNode的功能,下面就針對于上面的羅列的幾點功能進行詳細的說明:
2.2.1、NameNode
1、NameNode元數(shù)據(jù)信息
??????元數(shù)據(jù)信息包括:文件名,文件目錄結(jié)構(gòu),文件屬性(生成時間-副本-權(quán)限),每個文件的塊信息,以及每一個塊的Datanode的位置;每個文件和數(shù)據(jù)塊之間的引用關(guān)系(文件、block、DataNode之間的映射關(guān)系)會定期的保存到本地磁盤(這部分主要是FsImage文件和Edits文件完成)
2、NameNode文件操作
??????NameNode負責(zé)文件元數(shù)據(jù)的操作,DataNode負責(zé)實際處理文件內(nèi)容的讀寫請求;其中數(shù)據(jù)流不用經(jīng)過NameNode,但會詢問它跟那個DataNode建立聯(lián)系。
3、NameNode副本
??????文件數(shù)據(jù)塊到底存放到哪些DataNode上,是由NameNode決定的(NameNode文件操作中最后一句話),NN會根據(jù)全局情況做出放置副本的決定;而副本創(chuàng)建的數(shù)量用戶自己決定的。
4、NameNode心跳機制
??????NamoNode會全權(quán)管理數(shù)據(jù)塊的復(fù)制,周期性的接受心跳和塊的狀態(tài)報告信息(塊的狀態(tài)報告信息包含該DataNode上所有數(shù)據(jù)塊的列表;若接受到心跳信息(這個心跳信息是DataNode發(fā)送的),NameNode認(rèn)為DataNode工作正常,如果在10分鐘后還接受到不到DN的心跳,那么NameNode認(rèn)為DataNode已經(jīng)宕機 ,這時候NN準(zhǔn)備要把DN上的數(shù)據(jù)塊進行重新的復(fù)制;而塊的狀態(tài)報告包含了一個DN上所有數(shù)據(jù)塊的列表,blocks report(塊的狀態(tài)報告信息) 每個1小時發(fā)送一次。
2.2.2、DataNode
DataNode提供真實文件數(shù)據(jù)的存儲服務(wù)。
2.3、HDFS副本機制
2.3.1、HDFS使用數(shù)據(jù)塊作為存儲的邏輯單元的原因
??????所有的文件都是一Block塊的方式存放在HDFS文件系統(tǒng)當(dāng)中,作用如下:
(1)一個文件有可能大于集群中任意一個磁盤,引入塊機制,可以很好的解決這個問題。
(2)使用塊作為文件存儲的邏輯單位可以簡化存儲子系統(tǒng)
(3)塊非常適合用于數(shù)據(jù)備份進而提供數(shù)據(jù)容錯能力。
2.3.2、副本存放策略
??????HDFS文件系統(tǒng)中的副本存放策略:以默認(rèn)的副本數(shù)==3為例子:
1、第一個副本塊存本機
2、第二個副本塊存跟本機同機架內(nèi)的其他服務(wù)器節(jié)點
3、第三個副本塊存不同機架的一個服務(wù)器節(jié)點上
2.4、HDFS的命令行
2.4.1、HDFS基本命令
2.4.2、HDFS高級命令
2.5、HDFS文件寫入和讀取過程
2.5.1、寫過程

過程說明:
1、Client 發(fā)起文件上傳請求,通過 RPC 與 NameNode 建立通訊,NameNode 檢查目標(biāo)文件是否已存在, 父目錄是否存在,返回是否可以上傳。
2、Client 請求NameNode第一個 block 該傳輸?shù)侥男?DataNode 服務(wù)器上。
3、NameNode 根據(jù)配置文件中指定的備份數(shù)量及副本存放策略進行文件分配,返回可用的DataNode 的地址如:A,B,C
4、Client 請求 3 臺 DataNode 中的一臺 A 上傳數(shù)據(jù)(本質(zhì)上是一個 RPC 調(diào)用,建立 pipeline,A 收到請求會繼續(xù)調(diào)用 B, 然后 B 調(diào)用 C,將整個 pipeline 建立完成,后逐級返回 client
5、Client 開始往 A 上傳第一個 block(先從磁盤讀取數(shù)據(jù)放到一個本地內(nèi)存緩存),以packet 為單位(默認(rèn)64K),A 收到一個 packet 就會傳給 B,B 傳給 C。A 每傳一個 packet 會放入一個應(yīng)答隊列等待應(yīng)答
6、數(shù)據(jù)被分割成一個個 packet 數(shù)據(jù)包在 pipeline 上依次傳輸,在 pipeline 反方向上,逐個發(fā)送 ack(命令正確應(yīng)答),最終由 pipeline 中第一個 DataNode 節(jié)點 A 將 pipeline ack 發(fā)送給 Client
7、當(dāng)一個 block 傳輸完成之后,Client 再次請求 NameNode 上傳第二個 block 到過程1。
2.5.2、讀過程

- Client向NameNode發(fā)起RPC請求,來確定請求文件block所在的位置;
- NameNode會視情況返回文件的部分或者全部block列表,對于每個block,NameNode 都會返回含有該 block 副本的 DataNode 地址; 這些返回的 DN 地址,會按照集群拓撲結(jié)構(gòu)得出 DataNode 與客戶端的距離,然后進行排序,排序兩個規(guī)則:網(wǎng)絡(luò)拓撲結(jié)構(gòu)中距離Client 近的排靠前;心跳機制中超時匯報的 DN 狀態(tài)為 STALE,這樣的排靠后;
- Client 選取排序靠前的 DataNode 來讀取 block,如果客戶端本身就是DataNode,那么將從本地直接獲取數(shù)據(jù)(短路讀取特性);
- 底層上本質(zhì)是建立 Socket Stream(FSDataInputStream),重復(fù)的調(diào)用父類DataInputStream 的 read 方法,直到這個塊上的數(shù)據(jù)讀取完畢;
- 當(dāng)讀完列表的 block 后,若文件讀取還沒有結(jié)束,客戶端會繼續(xù)向NameNode 獲取下一批的 block 列表;
- 讀取完一個 block 都會進行 checksum 驗證,如果讀取 DataNode 時出現(xiàn)錯誤,客戶端會通知 NameNode,然后再從下一個擁有該 block 副本的DataNode 繼續(xù)讀。
- read 方法是并行的讀取 block 信息,不是一塊一塊的讀?。籒ameNode 只是返回Client請求包含塊的DataNode地址,并不是返回請求塊的數(shù)據(jù);
- 最終讀取來所有的 block 會合并成一個完整的最終文件。
2.6、HDFS的元數(shù)據(jù)輔助管理
在Hadoop集群中,NameNode的所有元數(shù)據(jù)信息都保存在了FsImage與Eidts文件當(dāng)中。
2.6.1、FsImage和Edits詳解
Edits
1、edits 存放了客戶端最近一段時間的操作日志
2、客戶端對 HDFS 進行寫文件時會首先被記錄在 edits 文件中
3、edits 修改時元數(shù)據(jù)也會更新
Fsimage
1、NameNode 中關(guān)于元數(shù)據(jù)的鏡像, 一般稱為檢查點, fsimage 存放了一份比較完整的元數(shù)據(jù)信息
2、因為 fsimage 是 NameNode 的完整的鏡像, 如果每次都加載到內(nèi)存生成樹狀拓撲結(jié)構(gòu),這是非常耗內(nèi)存和CPU, 所以一般開始時對 NameNode 的操作都放在 edits 中
3、fsimage 內(nèi)容包含了 NameNode 管理下的所有 DataNode 文件及文件 block 及 block所在的 DataNode 的元數(shù)據(jù)信息
4、隨著 edits 內(nèi)容增大, 就需要在一定時間點和 fsimage 合并。
2.6.2、FsImage和Edits相關(guān)操作
2.6.3、SecondaryNameNode 如何輔助管理 fsimage 與 edits 文件?
SecondaryNameNode 定期合并Fsimage和edits,把edits大小控制在一個范圍內(nèi);

合并步驟:
- SecondaryNameNode 通知 NameNode 切換成editlog.new
- SecondaryNameNode 從 NameNode 中獲得 fsimage 和 舊editlog(通過http方式)
- SecondaryNameNode 將 fsimage 載入內(nèi)存, 然后開始合并 editlog, 合并之后成為新的fsimage
- SecondaryNameNode 將新的 fsimage 發(fā)回給 NameNode
- NameNode 用新的 fsimage 替換舊的 fsimage,Edits替換edits.new。
特點:
1、完成合并的是 SecondaryNameNode, 會請求 NameNode 停止使用 edits, 暫時將新寫操作放入一個新的文件中 edits.new;
2、SecondaryNameNode 從 NameNode 中通過 Http GET 獲得 edits, 因為要和 fsimage 合并, 所以也是通過 Http Get 的方式把 fsimage 加載到內(nèi)存, 然后逐一執(zhí)行具體對文件系統(tǒng)的操作,與 fsimage 合并, 生成新的 fsimage, 然后通過 Http POST 的方式把 fsimage 發(fā)送給NameNode;NameNode 從 SecondaryNameNode 獲得了 fsimage 后會把原有的 fsimage 替換為新的 fsimage, 把 edits.new 變成 edits. 同時會更新 fstime;
3、Hadoop 進入安全模式時需要管理員使用 dfsadmin 的 save namespace 來創(chuàng)建新的檢查點;
4、SecondaryNameNode 在合并 edits 和 fsimage 時需要消耗的內(nèi)存和 NameNode 差不多, 所
以一般把 NameNode 和 SecondaryNameNode 放在不同的機器上。
2.7、HDFS的API操作
2.7.1、Windows系統(tǒng)下的常見錯誤
1、缺少winutils.exe
2、缺少Hadoop.dll
上面兩個問題都是在Window系統(tǒng)下缺少Hadoop運行環(huán)境導(dǎo)致的。解決方案:配置Hadoop環(huán)境變量,并重啟Windows。
2.7.2、使用文件系統(tǒng)方式訪問HDFS數(shù)據(jù)
1、獲取FileSystem的四種方式(四種方式差不多):
@Test
public void getFileSystem1(){
Configuration conf = new Configuration();
conf.set("fs.defaultFS","hdfs://master");
try {
FileSystem fileSystem = FileSystem.get(conf);
System.out.println(fileSystem.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void getFileSystem2(){
FileSystem fileSystem = null;
try {
fileSystem = FileSystem.get(new URI("hdfs://master:8020"), new Configuration());
System.out.println(fileSystem.toString());
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
@Test
public void getFileSystem3(){
Configuration conf = new Configuration();
conf.set("fs.defaultFS","hdfs://master");
try {
FileSystem fileSystem = FileSystem.newInstance(conf);
System.out.println(fileSystem.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void getFileSystem4(){
FileSystem fileSystem = null;
try {
fileSystem = FileSystem.newInstance(new URI("hdfs://master:8020"), new Configuration());
System.out.println(fileSystem.toString());
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
2、遍歷HDFS中所有文件:
@Test
public void listMyFile() throws Exception {
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://master:8020"), new Configuration());
RemoteIterator<LocatedFileStatus> locatedFileStatusRemoteIterator = fileSystem.listFiles(new Path("/"), false);
while (locatedFileStatusRemoteIterator.hasNext()){
LocatedFileStatus next = locatedFileStatusRemoteIterator.next();
System.out.println(next.getPath().toString());
}
}
3、創(chuàng)建文件夾:
public void mkdirs() throws Exception {
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://master:8020"), new Configuration());
boolean mkdirs = fileSystem.mkdirs(new Path("/hello/mydir/test"));
System.out.println(mkdirs);
fileSystem.close();
}
4、從HDFS上面下載文件到本地:
public void getFileToLocal() throws Exception {
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://master:8020"), new Configuration());
FSDataInputStream fsDataInputStream = fileSystem.open(new Path("/a.txt"));
FileOutputStream fileOutputStream = new FileOutputStream(new File("d:\\timer.txt"));
IOUtils.copy(fsDataInputStream,fileOutputStream);
IOUtils.closeQuietly(fileOutputStream);
IOUtils.closeQuietly(fsDataInputStream);
fileSystem.close();
}
5、HDFS文件上傳:
@Test
public void putData() throws Exception {
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://master:8020"), new Configuration());
fileSystem.copyFromLocalFile(new Path("file:///d:\\timer.txt"),new Path("/hello/mydir/test"));
fileSystem.close();
}
7、小文件合并:
@Test
public void mergeFile() throws Exception {
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://master:8020"), new Configuration());
FSDataOutputStream outputStream = fileSystem.create(new Path("/bigFile.txt"));
LocalFileSystem localFileSystem = FileSystem.getLocal(new Configuration());
FileStatus[] fileStatuses = localFileSystem.listStatus(new Path("file:///D:\\input\\hdfsMerge"));
for (FileStatus fileStatus : fileStatuses) {
FSDataInputStream inputStream = localFileSystem.open(fileStatus.getPath());
IOUtils.copy(inputStream,outputStream);
IOUtils.closeQuietly(inputStream);
}
IOUtils.closeQuietly(outputStream);
localFileSystem.close();
fileSystem.close();
}