本文主要介紹了hdfs的基本原理、hdfs基本操作、hdfs的讀取數據流程、namenode工作機制,rpc編程以及常見的兩種必會的面試題等,旨在於全面深入的理解HDFS的基本工作流程並可以開發出簡易的HDFS的程序。
一、HDFS簡介
Hadoop分佈式文件系統(HDFS)被設計成適合運行在通用硬件(commodity hardware)上的分佈式文件系統。HDFS體系結構中有兩類節點,一類是NameNode,又叫"元數據節點";另一類是DataNode,又叫"數據節點"。這兩類節點分別承擔Master和Worker具體任務的執行節點。總的設計思想:分而治之——將大文件、大批量文件,分佈式存放在大量獨立的服務器上,以便於採取分而治之的方式對海量數據進行運算分析。
HDFS是一個主/從(Mater/Slave)體系結構,從最終用戶的角度來看,它就像傳統的文件系統一樣,可以通過目錄路徑對文件執行CRUD(Create、Read、Update和Delete)操作。但由於分佈式存儲的性質,HDFS集羣擁有一個NameNode和一些DataNode。NameNode管理文件系統的元數據,DataNode存儲實際的數據。客戶端通過同NameNode和DataNodes的交互訪問文件系統。客戶端聯繫NameNode以獲取文件的元數據,而真正的文件I/O操作是直接和DataNode進行交互的。
HDFS一般是用來“一次寫入,多次讀取”,不適合做實時交互性很強的事情,不適合存儲大量小文件(當然,如果你偏要存大量小文件的話本文末尾會有解決方案).
二、HDFS工作原理
2.1基本原理
1 分佈式文件系統,它所管理的文件是被切塊存儲在若干臺datanode服務器上.
2 hdfs提供了一個統一的目錄樹來定位hdfs中的文件,客戶端訪問文件時只要指定目錄樹的路徑即可,不用關心文件的具體物理位置.
3 每一個文件的每一個切塊,在hdfs集羣中都可以保存多個備份(默認3份),在hdfs-site.xml中,dfs.replication的value的數量就是備份的數量.
4 hdfs中有一個關鍵進程服務進程:namenode,它維護了一個hdfs的目錄樹及hdfs目錄結構與文件真實存儲位置的映射關係(元數據).而datanode服務進程專門負責接收和管理"文件塊"-block.默認大小爲128M(可配置),(dfs.blocksize).(老版本的hadoop的默認block是64M的)
2.2 常用操作
hdfs的根目錄,通過這條命令可以查看hdfs存儲的文件,在hadoop的安裝目錄下面:
- bin/hdfs dfs -ls /<pre name="code" class="html">hdfs dfs -chmod 600 /test.txt //設置文件權限
- bin/hdfs dfs -df -h / //查看磁盤空間
- bin/hdfs dfs -mkdir /aaa //新建文件夾
- bin/hdfs dfs -tail //查看文件尾部
hdfs塊的存儲位置位於:(在hadoop的根目錄下面的tmp文件夾中)hadoop-2.5.2/tmp/hadoop/dfs/data/current/BP-33587274-127.0.0.1-1465370300743/current/finalized
從圖中我們可以看到hdfs存儲的塊的,當然這裏面的兩個塊通過合並可以形成一個新的文件,然後可以通過解壓進行驗證。
- touch full hadoop.tar.gz
- cat blk_1073741825 >> hadoop.tar.gz
- cat blk_1073741826 >> hadoop.tar.gz
- tar -xvzf hadoop.tar.gz
同樣,在hadoop的安裝目錄中,直接用命令來存取文件可以通過下面的命令:
存文件:
./hdfs dfs -put /home/admin1/桌面/test.txt hdfs://localhost:9000/
取文件:
./hdfs dfs -get hdfs://localhost:9000/test.txt
hdfs的權限控制:
客戶端的操作命令
- bin/hdfs dfs -chown aa:bgroup /test.txt
//將test.txt文件的權限改爲aa用戶的bgroup組.
(因爲我根本就沒有這個組和用戶,但是也可以修改成功,所以說明hadoop本身對權限的控制不是很嚴格)
2.3 HDFS的shell操作
--appendToFile ----追加一個文件到已經存在的文件末尾
-->hadoop fs -appendToFile ./hello.txt hdfs://hadoop-server01:9000/hello.txt
可以簡寫爲:
Hadoop fs -appendToFile ./hello.txt /hello.txt
-cat ---顯示文件內容
-->hadoop fs -cat /hello.txt
-chgrp
-chmod
-chown
上面三個跟linux中的用法一樣
-->hadoop fs -chmod 666 /hello.txt
-copyFromLocal #從本地文件系統中拷貝文件到hdfs路徑去
-->hadoop fs -copyFromLocal ./jdk.tar.gz /aaa/
-copyToLocal #從hdfs拷貝到本地
Eg: hadoop fs -copyToLocal /aaa/jdk.tar.gz
-count #統計一個指定目錄下的文件節點數量
-->hadoop fs -count /aaa/
-cp #從hdfs的一個路徑拷貝hdfs的另一個路徑
hadoop fs -cp/aaa/jdk.tar.gz /bbb/jdk.tar.gz.2
-createSnapshot
-deleteSnapshot
-renameSnapshot
以上三個用來操作hdfs文件系統目錄信息快照
-->hadoop fs -createSnapshot /
-df #統計文件系統的可用空間信息
-du
-->hadoop fs -df -h /
-->hadoop fs -du -s -h /aaa/*
-get #等同於copyToLocal,就是從hdfs下載文件到本地
-getmerge #合併下載多個文件
--> 比如hdfs的目錄 /aaa/下有多個文件:log.1, log.2,log.3,...
hadoop fs -getmerge /aaa/log.* ./log.sum
-help #輸出這個命令參數手冊
-ls #顯示目錄信息
-->hadoop fs -ls hdfs://hadoop-server01:9000/
這些參數中,所有的hdfs路徑都可以簡寫
-->hadoop fs -ls / 等同於上一條命令的效果
-mkdir #在hdfs上創建目錄
-->hadoop fs -mkdir -p /aaa/bbb/cc/dd
-moveFromLocal #從本地剪切粘貼到hdfs
-moveToLocal #從hdfs剪切粘貼到本地
-mv #在hdfs目錄中移動文件
-put #等同於copyFromLocal
-rm #刪除文件或文件夾
--> hadoop fs -rm -r/aaa/bbb/
-rmdir #刪除空目錄
-setrep #設置hdfs中文件的副本數量
-->hadoop fs -setrep 3 /aaa/jdk.tar.gz
-stat #顯示一個文件或文件夾的元信息
-tail #顯示一個文件的末尾
-text #以字符形式打印一個文件的內容
三、HDFS寫入數據流程解析
3.1 基本原理
hdfs的數據寫入是非常複雜的過程,下面來看一下簡要的步驟。
那麼問題來了,如果他們之間的一個datanode突然壞掉了怎麼辦。
1、如果傳輸過程中,有某個datanode出現了故障,那麼當前的pipeline會被關閉,出現故障的datanode會從當前的pipeline中移除,剩餘的block會繼續剩下的datanode中繼續以pipeline的形式傳輸,同時Namenode會分配一個新的datanode,保持replicas設定的數量。
2、關閉pipeline,將ack queue中的數據塊放入data queue的開始。
3、當前的數據塊在已經寫入的數據節點中被元數據節點賦予新的標示,則錯誤節點重啓後能夠察覺其數據塊是過時的,會被刪除。
4、失敗的數據節點從pipeline中移除,另外的數據塊則寫入pipeline中的另外兩個數據節點。
5、元數據節點則被通知此數據塊是複製塊數不足,將來會再創建第三份備份。
6、客戶端調用create()來創建文件
7、DistributedFileSystem用RPC調用元數據節點,在文件系統的命名空間中創建一個新的文件。
8、元數據節點首先確定文件原來不存在,並且客戶端有創建文件的權限,然後創建新文件。
9、DistributedFileSystem返回DFSOutputStream,客戶端用於寫數據。
10、客戶端開始寫入數據,DFSOutputStream將數據分成塊,寫入data queue。
11、Data queue由Data Streamer讀取,並通知元數據節點分配數據節點,用來存儲數據塊(每塊默認複製3塊)。分配的數據節點放在一個pipeline裏。
12、Data Streamer將數據塊寫入pipeline中的第一個數據節點。第一個數據節點將數據塊發送給第二個數據節點。第二個數據節點將數據發送給第三個數據節點。
13、DFSOutputStream爲發出去的數據塊保存了ack queue,等待pipeline中的數據節點告知數據已經寫入成功。
3.2 編碼實踐
- @Test
- public void testUpload() throws IOException, InterruptedException, URISyntaxException{
- Configuration conf = new Configuration();
- //to get a client of the hdfs system
- FileSystem fs = FileSystem.get(new URI("hdfs://ubuntu2:9000"),conf,"admin1");
- fs.copyFromLocalFile(new Path("/home/admin1/hadoop/eclipse.tar.gz"), new Path("/"));
- fs.close();
- }
四、HDFS讀取數據流程解析
4.1 基本原理
讀取的流程比寫入的流程就簡單很多了。
4.2 編碼實踐
- /*public static void main(String[] args) throws IOException {
- //download from hdfs
- Configuration conf = new Configuration();
- conf.set("fs.defaultFS", "hdfs://ubuntu2:9000/");
- conf.set("dfs.blocksize", "64");
- //to get a client of the hdfs system
- FileSystem fs = FileSystem.get(conf);
- fs.copyToLocalFile(new Path("hdfs://ubuntu2:9000/test.txt"), new Path("/home/admin1/Downloads/test1.txt"));
- fs.close();
- }*/
- @Test
- public void rmove() throws IOException, InterruptedException, URISyntaxException{
- Configuration conf = new Configuration();
- FileSystem fs = FileSystem.get(new URI("hdfs://ubuntu2:9000"),conf,"admin1");
- fs.delete(new Path("/test.txt"));
- fs.close();
- }
五、NameNode工作機制
元數據放在內存中,同時需要備份,通過日誌的記錄(對元數據有修改操作時)寫到磁盤中,用edits_ingropress,定期dump一次,當一旦斷電時,很難恢復,
所以我們就用sercondName定期把操作日誌下載和內存鏡像文件,然後就定期合併操作記錄,(日誌+fsimage)然後生成一個新的namenode鏡像文件 (fs.image.ckpt)
最後上傳給namenode,然後namenode重命名爲fsimage.
元數據管理機制:
1、元數據有3中存儲形式:內存、edits日誌、fsimage
2、最完整最新的元數據一定是內存中的這一部分
六、RPC編程
使用 RPC 編程是在客戶機和服務器實體之間進行可靠通信的最強大、最高效的方法之一。它爲在分佈式計算環境中運行的幾乎所有應用程序提供基礎。任何 RPC 客戶機-服務器程序的重要實體都包括 IDL 文件(接口定義文件)、客戶機 stub、服務器 stub 以及由客戶機和服務器程序共用的頭文件。客戶機和服務器 stub 使用 RPC 運行時庫通信。RPC 運行時庫提供一套標準的運行時例程來支持 RPC 應用程序。在一般的應用程序中,被調用的過程在相同的地址空間中運行,並把結果返回給發出調用的過程。在分佈式環境中,客戶機和服務器在不同的機器上運行,客戶端調用在服務器端運行的過程,並把結果發送回客戶機。這稱爲遠程過程調用 (RPC),是 RPC 編程的基礎。
以下實例是用來模擬用戶登陸的過程。
在linux中的eclipse。新建一個java工程,新建java文件:
RPCServer.java
- package hdfsTest;
- public interface RPCService {
- public static final long versionID=100L; //定義通信接口的版本號
- public String userLogin(String username,String password); //用戶名和密碼
- }
RPCServerImpl.java
- package hdfsTest;
- public class RPCServiceImpl implements RPCService {
- @Override
- public String userLogin(String username, String password) {
- return username+" logged in successfully!";
- }
- }
RPCControl.java
- package hdfsTest;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.ipc.RPC;
- //
- public class RPCController {
- public static void main(String[] args) throws IOException {
- RPCService serverceImpl=RPC.getProxy(RPCService.class,100,new InetSocketAddress("ubuntu2",10000),new Configuration());
- <span style="white-space:pre"> </span>//100是指前面設置的版本號,InetSocketAddress中的是hdfs主機名和10000是通信端口
- String result=serverceImpl.userLogin("aaa", "aaa"); //設置用戶用戶名和密碼
- System.out.println(result);
- }
- }
ServerRunner.java
- package hdfsTest;
- 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.Builder;
- import org.apache.hadoop.ipc.Server;
- public class ServerRunner {
- public static void main(String[] args) throws HadoopIllegalArgumentException, IOException {
- Builder builder=new RPC.Builder(new Configuration());
- builder.setBindAddress("ubuntu2"); //hdfs的主機名
- builder.setPort(10000); //設置通信端口號
- builder.setProtocol(RPCService.class);
- builder.setInstance(new RPCServiceImpl());
- Server server=builder.build();
- server.start(); //開啓服務
- }
- }
運行結果:
七、常見問題分析
7.1.HDFS可不可以用來做網盤
1、容量成本太高,2、文件大小不確定,如果存大量小文件會造成很大的浪費 3、相對於網盤來說,文件讀寫的效率低 4、只適合一次寫入,多次讀取的操作
5、hdfs不支持文件內容修改,可支持往文件尾部追加內容。
7.2 HDFS大量小文件存儲
文章出處:
http://blog.csdn.net/sdksdk0/article/details/51622547