HDFS相關源碼剖析

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流來獲取到指定的文件信息。

 

 

DFSInputStreamBlockReader  介紹

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;

}

 

DFSOutputStreamPacket DataStreamerResponseProcessor介紹

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();

              }

 

  }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章