Hadoop源碼分析-HDFS寫數(shù)據(jù)的契約機制

每創(chuàng)建一個文件后,會為每個文件創(chuàng)建一個契約(lease)

先引入一個小的背景,假如多個客戶端同時要并發(fā)的寫Hadoop HDFS上的一個文件,這個事兒能成嗎? 明顯不可以接受啊,因為HDFS上的文件是不允許并發(fā)寫的,比如并發(fā)的追加一些數(shù)據(jù)什么。 所以HDFS里有一個機制,叫做文件契約機制。 也就是說,同一時間只能有一個客戶端獲取NameNode上面一個文件的契約,然后才可以向獲取契約的文件寫 入數(shù)據(jù)。 此時如果其他客戶端嘗試獲取文件契約的時候,就獲取不到,只能干等著。 通過這個機制,可以保證同一時間只有一個客戶端在寫一個文件。 在獲取到了文件契約之后,在寫文件的過程期間,那個客戶端需要開啟一個線程,不停的發(fā)送請求給 NameNode進行文件續(xù)約,告訴NameNode: NameNode大哥,我還在寫文件啊,你給我一直保留那個契約好嗎? 而NameNode內(nèi)部有一個專門的后臺線程,負責(zé)監(jiān)控各個契約的續(xù)約時間。 如果某個契約很長時間沒續(xù)約了,此時就自動過期掉這個契約,讓別的客戶端來寫。

1. 添加契約

  1. FSNamesystem.startFileInternal(),可以看到leaseManager.addLease()就是開始添加契約代碼。
    image
  2. LeaseManager.addLease(),
  • 1:這里如果沒有lease,那就new一個,然后把這個lease存儲到一個TreeMap和一個TreeSet里,如果已經(jīng)有lease對象了,那么就調(diào)用renewLease();
  • 2:sortedLeasesByPath是個TreeMap,這里就是把剛才獲得的leasesrc變量都put到這個map中(這個src應(yīng)該就是文件的路徑);
  • 3:paths是個TreeSet,就是把src存儲到一個set中。
  • 后面讓我們看看renewLease()方法
    image
  1. LeaseManager.renewLease();1:先把lease從set中移除;2:然后調(diào)用lease.renew(),3:最后又把lease添加到set中。直接看2。

    image

  2. Lease.renew(),1這里就是把更新了leaselastUpdate;2這里簡單注意下LeaseLeaseManager的內(nèi)部類。

    image

在第2步這里我們說到,使用到了TreeMap那么肯定就是要對Lease進行排序了。那么是根據(jù)什么排序的呢?我們看看Lease的實現(xiàn)。

  1. Lease,可以看到Lease實現(xiàn)了Comparable接口,實現(xiàn)了compareTo方法,并且進行比較的字段是lastUpdate
    image

到此我們可以總結(jié)下了,所謂文件的契約其實就是創(chuàng)建一個Lease對象,然后把這個對象存儲到一個TreeMap和一個TreeSet中。使用集合的原因是,因為有很多契約,但是對應(yīng)的每個文件就一個契約,而使用TreeMapTreeSet的原因是因為要為契約按照最后更新時間進行升序排序。

  1. 那么這些代碼都執(zhí)行完之后,會從第3步的代碼繼續(xù)執(zhí)行,我們繼續(xù)看看后面有什么。再后面,就是又進行Editlog日志同步了。如果進去看看就是Editlog的雙緩沖機制了。我們之前講過就不看了。
    image

2. 契約的續(xù)約-類似心跳

  1. 那么這些都執(zhí)行完了,客戶端創(chuàng)建文件的流程也就結(jié)束了。不過我們還得看下客戶端那邊的代碼??梢钥吹娇蛻舳诉@里再創(chuàng)建完文件后,又創(chuàng)建了一個DFSOutputStream對象,然后又調(diào)用了DFSOutputStream的start方法。
    image
  2. DFSOutputStream.start(),這里有調(diào)用了DataStreamer.start(),而DataStreamer其實是個線程。DFSOutputStreamDataStreamer這兩類,是寫數(shù)據(jù)的核心類。我們后面在詳細聊。
    image
  3. 以上所有的代碼,都是DFSOutputStream.newStreamForCreate()所調(diào)用的。而后面又執(zhí)行了beginFileLease()方法。
    image
  4. DFSClient.beginFileLease(),這里其實就是續(xù)約
    image
  5. LeaseRenewer.put(),這里啟動了一個線程,run方法里調(diào)用了LeaseRenewer.this.run(id);
    image
  6. LeaseRenewer.run(),這里就是調(diào)用了renew();然后再后面就獲得了新的契約時間。那肯定renew()就是續(xù)約了。
    image
  7. LeaseRenewer.renew(),這邊的續(xù)約代碼寫的比較隱蔽。那就是在if那里,就是c.renewLease()。這里有個for循環(huán),可以看到是遍歷的DFSClient對象。也就是說這里應(yīng)該是給所有客戶端續(xù)約了。
    image
  8. 一看到下面的代碼,應(yīng)該就想到要去NameNodeRpcServer里去找了。
    image
  9. 這里的代碼就很簡答了。其實后面的代碼跟上面我們說的創(chuàng)建契約的代碼是一樣的。


    image

    image

    image

3. 契約續(xù)約的監(jiān)控

客戶端寫文件,總不能一直寫,總會寫完吧。所以光創(chuàng)建契約和續(xù)約怎么行呢。還得有個線程監(jiān)控寫完了移除契約的。LeaseManager有個內(nèi)部類Monitor就是做這事情的。

  1. Monitor也是個線程,所以,肯定執(zhí)行的是run方法了。然后是個for循環(huán),這里就是類似while(true),循環(huán)里關(guān)鍵的代碼就是這么一行needSync = checkLeases();,最后有個sleep,sleep時間是2秒。

    image

  2. checkLeases();,1:這里是獲得最老的契約;2:這是判斷如果最老的契約沒有超時,那么直接break了;這個超時時間是1小時。

    image

  3. 如果超時了,那么就執(zhí)行移除契約的操作removeLease(leaseToCheck, p);。具體就不看了,肯定是從剛才那個treemaptreeset里移除這個契約唄。

    image

  4. 到此我們畫個圖,簡單總結(jié)下契約這事:


    image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容