每創(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. 添加契約
-
FSNamesystem.startFileInternal(),可以看到leaseManager.addLease()就是開始添加契約代碼。
image -
LeaseManager.addLease(),
-
1:這里如果沒有lease,那就new一個,然后把這個lease存儲到一個TreeMap和一個TreeSet里,如果已經(jīng)有lease對象了,那么就調(diào)用renewLease(); -
2:sortedLeasesByPath是個TreeMap,這里就是把剛才獲得的lease和src變量都put到這個map中(這個src應(yīng)該就是文件的路徑); -
3:paths是個TreeSet,就是把src存儲到一個set中。 - 后面讓我們看看
renewLease()方法
image
-
LeaseManager.renewLease();1:先把lease從set中移除;2:然后調(diào)用lease.renew(),3:最后又把lease添加到set中。直接看2。
image -
Lease.renew(),1這里就是把更新了lease的lastUpdate;2這里簡單注意下Lease是LeaseManager的內(nèi)部類。
image
在第2步這里我們說到,使用到了TreeMap那么肯定就是要對Lease進行排序了。那么是根據(jù)什么排序的呢?我們看看Lease的實現(xiàn)。
-
Lease,可以看到Lease實現(xiàn)了Comparable接口,實現(xiàn)了compareTo方法,并且進行比較的字段是lastUpdate
image
到此我們可以總結(jié)下了,所謂文件的契約其實就是創(chuàng)建一個Lease對象,然后把這個對象存儲到一個TreeMap和一個TreeSet中。使用集合的原因是,因為有很多契約,但是對應(yīng)的每個文件就一個契約,而使用TreeMap和TreeSet的原因是因為要為契約按照最后更新時間進行升序排序。
- 那么這些代碼都執(zhí)行完之后,會從第3步的代碼繼續(xù)執(zhí)行,我們繼續(xù)看看后面有什么。再后面,就是又進行Editlog日志同步了。如果進去看看就是
Editlog的雙緩沖機制了。我們之前講過就不看了。
image
2. 契約的續(xù)約-類似心跳
- 那么這些都執(zhí)行完了,客戶端創(chuàng)建文件的流程也就結(jié)束了。不過我們還得看下客戶端那邊的代碼??梢钥吹娇蛻舳诉@里再創(chuàng)建完文件后,又創(chuàng)建了一個
DFSOutputStream對象,然后又調(diào)用了DFSOutputStream的start方法。
image -
DFSOutputStream.start(),這里有調(diào)用了DataStreamer.start(),而DataStreamer其實是個線程。DFSOutputStream與DataStreamer這兩類,是寫數(shù)據(jù)的核心類。我們后面在詳細聊。
image - 以上所有的代碼,都是
DFSOutputStream.newStreamForCreate()所調(diào)用的。而后面又執(zhí)行了beginFileLease()方法。
image -
DFSClient.beginFileLease(),這里其實就是續(xù)約
image -
LeaseRenewer.put(),這里啟動了一個線程,run方法里調(diào)用了LeaseRenewer.this.run(id);
image -
LeaseRenewer.run(),這里就是調(diào)用了renew();然后再后面就獲得了新的契約時間。那肯定renew()就是續(xù)約了。
image -
LeaseRenewer.renew(),這邊的續(xù)約代碼寫的比較隱蔽。那就是在if那里,就是c.renewLease()。這里有個for循環(huán),可以看到是遍歷的DFSClient對象。也就是說這里應(yīng)該是給所有客戶端續(xù)約了。
image - 一看到下面的代碼,應(yīng)該就想到要去
NameNodeRpcServer里去找了。
image -
這里的代碼就很簡答了。其實后面的代碼跟上面我們說的創(chuàng)建契約的代碼是一樣的。
image
image
image
3. 契約續(xù)約的監(jiān)控
客戶端寫文件,總不能一直寫,總會寫完吧。所以光創(chuàng)建契約和續(xù)約怎么行呢。還得有個線程監(jiān)控寫完了移除契約的。LeaseManager有個內(nèi)部類Monitor就是做這事情的。
-
Monitor也是個線程,所以,肯定執(zhí)行的是run方法了。然后是個for循環(huán),這里就是類似while(true),循環(huán)里關(guān)鍵的代碼就是這么一行needSync = checkLeases();,最后有個sleep,sleep時間是2秒。
image -
checkLeases();,1:這里是獲得最老的契約;2:這是判斷如果最老的契約沒有超時,那么直接break了;這個超時時間是1小時。
image -
如果超時了,那么就執(zhí)行移除契約的操作
removeLease(leaseToCheck, p);。具體就不看了,肯定是從剛才那個treemap和treeset里移除這個契約唄。
image -
到此我們畫個圖,簡單總結(jié)下契約這事:
image




















