理解HDFS

綜述

當數據集的大小超過一臺獨立的物理計算機的存儲能力時,就有必要對它進行分區並存儲到若干臺單獨的計算機上。HDFS是hadoop的主要分佈式存儲系統,一個HDFS集羣主要包括NameNode用來管理文件系統的metadata,DataNode用來存儲實際的數據。下面是HDFS的一些特色java

  • 1.Hadoop包括HDFS是一個分佈式存儲和分佈式計算的架構,部署在商用硬件上面,它具備容錯性、可擴展和容易擴大規模等特色。MapReduce做爲Hadoop的一個組件常被用於處理大規模的分佈式應用
  • 2.HDFS的默認configuration可以適用於多數環境,除非是超大規模的集羣多是須要進行微調
  • 3.Hadoop是由java語言開發,具備容易移植的特色
  • 4.Hadoop能夠經過shell來控制HDFS,NameNode和DataNode能夠經過內置的web server來查看他們的運行狀態

HDFS體系結構

HDFS採用master/slave架構。一個HDFS集羣通常有一個Namenode和必定數目的Datanode組成。Namenode是一箇中心服務器,負責管理文件系統的namespace和客戶端對文件的訪問。Datanode在集羣中通常是一個節點,負責管理節點上它們附帶的存儲。在內部,一個文件其實分紅一個或多個block,這些block存儲在Datanode集合裏。Namenode執行文件系統的namespace操做,例如打開、關閉、重命名文件和目錄,同時決定block到具體Datanode節點的映射。Datanode在Namenode的指揮下進行block的建立、刪除和複製
node

NameNode的工做機制

NameNode是整個系統的管理節點,它維護這整個文件系統的文件目錄樹,文件/目錄的元信息和每一個文件對應的數據塊列表。接收用戶的操做請求
維護文件包括:web

  • 1.fsimage:整個文件系統的名字空間,包括數據塊到文件的映射、文件的屬性等
  • 2.edits:hadoop對於任何對文件系統元數據產生修改的操做
  • 3.fstime:保存最近一次checkpoint的時間
  • 4.NN始終再內存中保存metadata,用於處理請求
  • 5.當有"寫"請求到來時,NN會首先寫到edit log到磁盤(向edits文件中寫日誌)成功後返回後纔會去修改內存,並向客戶端返回
  • 6.NN會維護一個fsimage文件,也就是metadata的鏡像,可是fsimage不會隨時與NN內存中的metadata保持一致,而是每隔一段時間經過合併edits文件來更新內容(secondary NN是用來合併fsimage和edits文件來更新NN的metadata)

Secondary NameNode(僞分佈式或者非HA)

當NameNode啓動時,它首先須要從fsimage讀取HDFS的狀態,而後從edit log file讀取edits的信息。NameNode再將新的HDFS的狀態寫入到fsimage和新建一個空的edits file。在啓動階段,NameNode須要合併fsimageedits file,edits log file在一個忙碌的集羣中文件大小會變得很大,這會致使在重啓NameNode時候耗費較長的時間。
Secondary NameNode按期的合併fsimageedits log並保持edits log文件在一個適度的大小,它每每運行在不一樣的機器上面。在啓動Secondary NameNode上checkpoint過程主要收到兩個參數的 影響shell

  • dfs.namenode.checkpoint.period:默認的是1 hour,兩次連續的checkpoint的最大時間間隔
  • dfs.namenode.checkpoint.period:默認爲1 million 超過這個大小就督促進行checkpointed transactions,即便時間間隔還沒到
    小程序

    HDFS讀取文件過程

  • 1.初始化FileSystem,而後客戶端(client)使用FileSystem的open()函數打開文件
  • 2.FileSystem用RPC調用元數據節點,獲得文件的數據塊信息,即知道數據塊被存儲在哪幾個DataNode上
  • 3.DistributedFileSystem返回一個FSDataInputStream對象給客戶端,以便讀取數據,客戶端對這個輸入流調用 read()方法
  • 4.FSDataInputStream鏈接距離最近的datanode,反覆調用read方法,將數據返回到客戶端
  • 5.讀取結束。客戶端直接從DataNode上讀取文件,在此過程當中NN不參與文件的傳輸slave經過RPC請求master,master的方法會被調用服務器

    HDFS寫文件過程

  • 1.上傳文件test.log爲例子,client向NN發起寫的請求,經過RPC與NN創建鏈接
  • 2.NN會根據文件大小和文件塊配置狀況,返回給它所管理的DataNode的信息
  • 3.Client將文件劃分紅多個文件塊,根據DataNode的地址信息將數據寫入到DataNode中,假設副本數目爲3,Hadoop的默認分佈策略是:
    • a.在運行客戶端的節點上放1個副本,若是客戶端是在集羣以外就隨機選擇一個節點
    • b.第二個副本放在與第一個不一樣的機架上
    • c.第三個副本與第二個副本放在同一個機架上,且隨機選擇的另外一個節點上。

下圖是數據塊的分佈

上傳成功後NN始終在內存中保存metadata,用於處理讀請求,metadata主要存儲文件的名稱FileName,副本數量replicas,分多少block存儲block-ids,分別存儲在哪幾個節點上id2host架構

Hadoop中的RPC機制

上面講了那麼多不一樣角色之間的交互,這些進程間的交互都是經過RPC(Remote Procdure Call,遠程過程調用)來進行的,它容許一個進程去訪問另外一個進程的方法,這些對於用戶都是透明的,能夠說Hadoop的運行是創建在RPC基礎之上的,在瞭解RPC以前咱們須要先了解一項技術動態代理,動態代理能夠提供對另外一個對象的訪問,同時隱藏實際對象的具體事實,代理對象對客戶隱藏了實際對象。在Hadoop中DataNode端是經過得到NameNode的代理,經過該代理和NameNode進行通訊的,爲了更好的分析hadoop的RPC機制我想先分析一下動態代理是怎麼實現。
目前Java開發包中提供了對動態代理的支持,但如今只支持對接口的實現,咱們須要定義一個接口。併發

interface DynamicService {
    public void show();
}

當實現動態代理的時候,須要實現InvocationHandler類,而且覆寫其Invoker方法分佈式

class ClassA implements DynamicService {
    @Override
    public void show() {
        System.out.println("this is class A");
    }
}
class ClassB implements DynamicService {
    @Override
    public void show() {
        System.out.println("this is class B");
    }
}
class Invoker implements InvocationHandler {
    DynamicService  ds;
    public  Invoker(DynamicService  ds) {
        this.ds = ds;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
        //add some dynamic methods  here
        method.invoke(ds, args);
        //after
        return null;
    }
}

下面是例子的測試ide

public static void main(String[] args) {
        Invoker  inv1 = new Invoker(new ClassA());
        DynamicService ds = (DynamicService) Proxy.newProxyInstance(DynamicService.class.getClassLoader(),
                new Class[] {DynamicService.class}, inv1);
        ds.show();
        //添加另一個
        Invoker  inv2 = new Invoker(new ClassB());
        DynamicService dss = (DynamicService) Proxy.newProxyInstance(DynamicService.class.getClassLoader(),
                new Class[] {DynamicService.class}, inv2);
        dss.show();
    }

如今咱們就須要去實現Hadoop RPC,主要分爲如下幾步

  • 1.定義RPC協議:RPC是客戶端與服務器通訊接口,它定義了服務器對對外提供的服務接口
  • 2.實現RPC協議:Hadoop RPC一般方式一個Java 接口,用戶須要本身去實現接口
  • 3.構造和啓動器哦那個RPC服務:使用靜態類Builder構造一個RPC Server,並經過start()方法啓動該server
  • 4.構造RPC Client 併發送請求:使用RPC.getProxy()構造客戶端代理對象,經過代理對象訪問遠程端的方法
    有了上面的幾步,咱們如今就能夠本身寫一個RPC小程序了

    定義一個接口

public interface RPCService {
    public static final long versionID = 10010L;//版本號,不一樣版本號的RPC Client和Server之間不能相互通訊
    public String sayHi(String name);
}

實現接口並構造RPC Server服務

public class RPCServer implements RPCService {
    @Override
    public String sayHi(String name) {
        return "server response"+name;
    }
    public static void main(String[] args) throws HadoopIllegalArgumentException, IOException {
        Configuration conf = new Configuration();
        Server server = new RPC.Builder(conf)//
                .setProtocol(RPCService.class)//
                .setBindAddress("10.30.100.11")// 服務器地址
                .setPort(1234)//端口
                .setInstance(new RPCServer())// 設置託管對象
                .build(); 
        server.start();
    }
}

啓動服務器以後,服務器就在指定端口監聽客戶端的請求。服務器就處於監聽狀態等待客戶端請求到達。

構造RPC Client併發出請求

public class RPCClient {
    public static void main(String[] args) throws IOException {
        Configuration conf = new Configuration();
        //接口類型,就能夠調用接口中的方法.在客戶端獲取代理對象,有了代理對象就能夠調用目標對象(RPCServer)的方法了
        RPCService proxy = RPC.getProxy(RPCService.class, 
                10010, new InetSocketAddress("10.30.100.11", 1234), conf);//server的代理對象,server必須實現這個接口
        String result = proxy.sayHi("boyaa");
        System.out.println(result);
        RPC.stopProxy(proxy);
    }
}
相關文章
相關標籤/搜索