DFSClient | Namenode | Datanode 源碼分析順序圖:
DFSClient
|-------ClientProtocol
|-------DFSInputStream
|-------LocatedBlocks
|-------BlockReader
|-------DFSInputStream
|-------DFSOutputStream
|--------Packet
|--------pipeline
|--------DataStreamer
|--------ResponseProcessor
Datanode
|-------BlockSender
|-------DataXceiverServer
|-------- BlockReceiver
|-------DatanodeProtocol
|-------InterDatanodeProtocol
|-------ClientDatanodeProtocol
Namenode
|-------FSNameSystem
|-------FSDirectory
|-------FsImage
|-------LeaseManager
|-------HeartbeatManager
|-------HeartbeatThread
|------Monitor
一、DFSClient相關體系
DFSClient
|-------ClientProtocol
|-------DFSInputStream
|-------LocatedBlocks
|-------BlockReader
|-------DFSInputStream
|-------DFSOutputStream
|--------Packet
|--------pipeline
|--------DataStreamer
|--------ResponseProcessor
DFSClient類的介紹源碼:
* DFSClient can connect to a Hadoop Filesystem and
* perform basic file tasks. It uses the ClientProtocol
* to communicate with a NameNode daemon, and connects
* directly to DataNodes to read/write block data.
DFSClient這個類的作用是用於客戶端連接Hadoop的HDFS文件系統,並在HDFS文件系統上做基本的文件操作(任務)。這個類和通過RPC機制和HDFS文件系統通信的,具體的RPC通信協議接口類是:org.apache.hadoop.hdfs.protocol.ClientProtocol。
DFSClient通過PRC和namenode節點和datanode節點進行通信以及實現在datanode上執行讀/寫 block的操作
重要方法源碼:
static LocatedBlocks callGetBlockLocations(ClientProtocol namenode,
String src, long start, long length)
throws IOException {
try {
return namenode.getBlockLocations(src, start, length);
} catch(RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class,
UnresolvedPathException.class);
}
}
這個函數主要是通過跟namenode的交互,來完成從namenode取得用戶請求的file的元數據信息
ClientProtocol介紹
客戶端和namenode進行rpc通信的協議接口,實現類是org.apache.hadoop.hdfs.protocol.ClientProtocol。
DFSClient通過RPC機制和NameNode通信並獲得文件的元數據信息(數據的存放位置,校驗和,等等一些關鍵的信息),然後DFSClient再與DataNode通信(集羣中的其它機器)通過數據I/O流來獲取到指定的文件信息。
DFSInputStream、BlockReader 介紹
DFSInputStream是DFSClient用於讀取datanode上文件的輸入流。當DFSClient向namenode發送讀文件請求之後,namenode會將此file的元數據信息返回: LocatedBlocks。這個類,封裝了文件塊的信息(每個文件塊的大小、所在的datanode節點信息)。該輸入流根據這些信息找到對應的DataNode,然後調用BlockReader類的read()方法,讀取datanode節點上文件塊裏的內容。
重要變量源碼:
public class DFSInputStream {
private final DFSClient dfsClient;
private BlockReader blockReader = null;
private LocatedBlocks locatedBlocks = null;
private DatanodeInfo currentNode = null;
}
DFSOutputStream、Packet 、DataStreamer、ResponseProcessor介紹
DFSoutputStream類是一個輸出流,用於提供客戶端將本地文件(在客戶端的電腦上存的文件)上傳到HDFS上。當客戶端發送一個
寫請求後(create請求),比如上傳一個大文件。首先會通過DFSclient去找namenode,namenode首先會創建對應的目錄,然後返回給DSFClient輸出流。拿到輸出輸出流之後, 根據namenode返回的塊信息blocksize,將數據變成64kb的packet存儲到dataqueue隊列裏
DataStreamer會從dataQueue裏取出一個一個的packet進行數據傳輸,然後形成一個數據流管道 pipeline。然後將
數據流管道輸出給管道里的第一個datanode節點。第一個datanode節點將數據流管道信息交給管道中的第二個節點。直到管道里最後一個datanode完成存儲並返回ack確認後,通知resoponse線程,然後DataStreamer發送下一個packet。
DFSOutputStream裏有2個隊列和2個線程
兩個隊列相關代碼:
dataQueue是數據隊列,用於保存等待發送給datanode的數據包
private final LinkedList<Packet> dataQueue = new LinkedList<Packet>();
ackQueue是確認隊列,保存還沒有被datanode確認接收的數據包
private final LinkedList<Packet> ackQueue = new LinkedList<Packet>();
兩個線程:
streamer線程,不停的從dataQueue中取出數據包,發送給datanode
private DataStreamer streamer = new DataStreamer();
response線程,用於接收從datanode返回的反饋信息
private ResponseProcessor response = null;
在向DFSOutputStream中,寫入數據(通常是byte數組)的時候,實際的傳輸過程是:
1、文件數據以字節數組進行傳輸,每個byte[]被封裝成64KB的Packet,然後扔進dataQueue中
2、DataStreamer線程不斷的從dataQueue中取出Packet,通過socket發送給datanode(向blockStream寫數據)
發送前,將當前的Packet從dataQueue中移除,並addLast進ackQueue
3、ResponseProcessor線程從blockReplyStream中讀出從datanode的反饋信息
反饋信息很簡單,就是一個seqno,再加上每個datanode返回的標誌(成功標誌爲 DataTransferProtocol.OP_STATUS_SUCCESS)
通過判斷seqno(序列號,每個Packet有一個序列號),判斷datanode是否接收到正確的包。
只有收到反饋包中的seqno與ackQueue.getFirst()的包seqno相同時,說明正確。否則可能出現了丟包的情況。
4、如果一切OK,則從ackQueue中移出:ackQueue.removeFirst(); 說明這個Packet被datanode成功接收了。
DataStreamer類重要源碼:
private DataStreamer() {
isAppend = false;
stage = BlockConstructionStage.PIPELINE_SETUP_CREATE;
}
在DatStreamer開始發送block時,創建pipeline數據流管道
二、Datanode相關體系
Datanode
|-------BlockSender
|-------DataXceiverServer
|-------- BlockReceiver
|-------DatanodeProtocol
|-------InterDatanodeProtocol
|-------ClientDatanodeProtocol
BlockSender介紹
1.當用戶(客戶端)向HDFS讀取某一個文件時,客戶端會根據數據所在的位置轉向到具體的DataNode節點請求對應數據塊的數據,此時DataNode節點會用BlockSender向該客戶端發送數據;
2.當NameNode節點發現某個Block的副本不足時,它會要求某一個存儲了該Block的DataNode節點向其它DataNode節點複製該Block,當然此時仍然會採用流水線的複製方式,只不過數據來源變成了一個DataNode節點;
3.HDFS開了一個調節DataNode負載均衡的工具Balancer,當它發現某一個DataNode節點存儲的Block過多時,就會讓這個DataNode節點轉移一部分Blocks到新添加到集羣的DataNode節點或者存儲負載輕的DataNode節點上;
sh start-balancer.sh -t %10
百分數是磁盤使用偏差率,一般調節的範圍在10%~20%間。
4.DataNode會定期利用BlockSender來檢查一個Block的數據是否損壞。
DataXceiverServer介紹
datanode端是如何接受傳來的數據文件呢?
在datanode類裏,有一個線程類:
org.apache.hadoop.hdfs.server.datanode.DataXceiver。每當有client連接到datanode時,datanode會new一個DataXceiver,負責數據的傳輸工作。
在這個DataXceiver線程類裏:
相關代碼:
@Override
public void writeBlock(){
/** A class that receives a block and writes to its own disk, meanwhile
* may copies it to another site. If a throttler is provided,
* streaming throttling is also supported.
**/
BlockReceiver blockReceiver = null; // responsible for data handling
}
會調用 BlockReceiver 這個類,這個類是用於接收數據並寫入本地磁盤,還負責將數據傳輸給管道里下一個datanode節點。
DatanodeProtocol介紹
* Protocol that a DFS datanode uses to communicate with the NameNode.
* It's used to upload current load information and block reports.
這個類是datanode和namenode 實現RPC通信的接口協議,作用是datanode通過RPC機制連接namenode並彙報自身節點狀態信息。在這個接口類裏,一個非常重要的方法是:
相關代碼:
public HeartbeatResponse sendHeartbeat(){
}
這個是datanode向namenode發送心跳的方法,datanode週期性通過RPC調用sendHeartbeat向namenode彙報自身的狀態。
三、NameNode相關體系
Namenode
|-------FSNameSystem
|-------DFSConfigKeys
|-------FSDirectory
|-------FsImage
|-------LeaseManager
|-------HeartbeatManager
|-------HeartbeatThread
|------Monitor
Namenode介紹
* NameNode serves as both directory namespace manager and
* "inode table" for the Hadoop DFS. There is a single NameNode
* running in any DFS deployment. (Well, except when there
* is a second backup/failover NameNode, or when using federated NameNodes.)
*
* The NameNode controls two critical tables:
* 1) filename->blocksequence (namespace)
* 2) block->machinelist ("inodes")
*
* The first table is stored on disk and is very precious.
* The second table is rebuilt every time the NameNode comes up.
*
* 'NameNode' refers to both this class as well as the 'NameNode server'.
* The 'FSNamesystem' class actually performs most of the filesystem
* management.
Namenode類是Hadoop DFS文件系統的管理者類,用來管理HDFS的元數據信息。實際控制的是兩張表信息,分別是:
1.文件——文件塊信息
2.文件塊信息——存儲這個文件塊的機器列表信息
第一張表信息存儲在namenode節點的磁盤上,並且訪問是非常高效的(因爲namenode在啓動之後,會將數據加載到內存裏供快速訪問)
第二張表信息,在每次namenode重啓工作後,會重新建立。(這麼做目的是爲了確保塊信息存儲的準備性,實現機制時,當namenode重啓工作後,每個datanode節點通過rpc心跳向namenode彙報自身存儲的文件塊信息,然後namenode根據這些信息,重建第二張表信息,在此過程中,HDFS是處於安全模式的,即只能對外提供讀服務。當第二表信息重建完後,確認文件塊數量正確且都完整,則退出安全模式)
實際上,namenode更多的是充當一個領導角色或更像是一個協議類,它定義了需要做哪些事,而真正幹活的是FSNamesystem這個類。
在這個類裏,format()這個方法我們應該會很熟悉,這是namenode的格式化方法
format()方法:
/** Format a new filesystem. Destroys any filesystem that may already
* exist at this location. **/
public static void format(Configuration conf) throws IOException {
format(conf, true, true);
}
格式化方法的定義:生成一個全新的文件系統(DFS文件系統)。並且摧毀已經存在的舊數據信息。
format()方法源碼骨架:
private static boolean format(Configuration conf, boolean force boolean isInteractive) throws IOException {
//調用Fsnamesystem,獲取用戶配置的元數據信息存放路徑。如果不配置,則默認使用linux的/tmp/hadoop/dfs/name這個目錄。
//但是這個目錄是臨時目錄,非常危險,所以需要更改。
Collection<URI> nameDirsToFormat = FSNamesystem.getNamespaceDirs(conf);
List<URI> editDirsToFormat = FSNamesystem.getNamespaceEditsDirs(conf);
//然後調用FSImage類,在對應的目錄下,創建新的Fsimage文件和Edits文件
FSImage fsImage = new FSImage(conf, nameDirsToFormat, editDirsToFormat);
fsImage.format(fsn, clusterId);
}
FSimage介紹
把文件和目錄的元數據信息持久化地存儲到fsimage文件中,每次啓動時從中將元數據加載到內存中構建目錄結構樹,之後的操作記錄在edits 中
定期將edits與fsimage合併刷到fsimage中
loadFSImage(File curFile)用於從fsimage中讀入Namenode持久化的信息。
HeartbeatManager介紹
* Manage the heartbeats received from datanodes.
* The datanode list and statistics are synchronized
* by the heartbeat manager lock.
namenode通過這個類來管理各個datanode傳來的心跳。具體工作的線程是 HeartbeatThread這個線程類
Monitor介紹
這是HeartbeatManager類裏一個私有線程類,這個類的作用是週期性檢測各個Datanode的心跳,
在這個線程類裏run方法裏,有一個方法比較重要,就是 heartbeatCheck();這個方法的作用:
* Check if there are any expired heartbeats, and if so,
* whether any blocks have to be re-replicated.
* While removing dead datanodes, make sure that only one datanode is marked
* dead at a time within the synchronized section. Otherwise, a cascading
* effect causes more datanodes to be declared dead.
檢查是否有超時的datanode心跳。如果有的話,先判斷下在這個死亡節點上是否有數據塊需要進行復制。然後對這個datanode進行死亡標記。如果這個節點上有文件需要複製備份,則進行數據備份(滿足一個block在HDFS上存3份),複製完後然後再刪除這個死掉的datanode節點。從slaves列表中移除
此外,在進行死亡節點數據複製過程中,HDFS對外不提供寫服務,即客戶端此時是不能上傳文件到HDFS系統上的。直到數據複製完成(比如一個block達到3份),最後刪除此死亡節點後,纔對外提供寫服務。此過程中,不影響HDFS的查詢文件操作
相關代碼:
private class Monitor implements Runnable {
@Override
public void run() {
heartbeatCheck();
}
}