前言
前面文章筆者介紹過Hadoop社區爲了增加內部RPC的throughput,通過延時返回response的調整來提早釋放Server端的Handler資源,以此儘可能的把Handler的處理能力用在真正的RPC請求上。HDFS目前所使用的異步editlog機制正是使用了這個優化改進。這裏所說的HDFS異步editlog寫出並不是大家所簡單的認爲NameNode完全異步化寫出editlog到其JournalNode服務中,然後直接返回結果給client。那麼但異步寫出editlog失敗的時候,client怎麼能知道後面發生的結果呢?它只能接受之前收到的“預期”結果進行後續的操作了。因此我們說延時返回在這個場景就能發揮其強大的作用了。本文筆者來詳細聊聊這個延時返回機制如何在HDFS的異步editlog中發揮作用的。
現有HDFS的RPC正常請求處理
在講述HDFS異步editlog機制之前,我們先來看看正常HDFS RPC請求處理的過程:
- 1)Client端發起請求操作。
- 2)NameNode收到請求,然後執行對應RPC call請求方法的處理,以及操作處理成功情況下,需要額外寫出對應操作的editlog信息。
- 3)NameNode請求執行結束,在方法末尾操作執行logSync操作,寫出此操作對應的editlog到JournalNode中,至此一個完整的RPC調用操作結束。
- 4)NameNode返回結果回覆給Client。
簡單圖示過程如下:
以下是一個NameNode裏面的樣例RPC call請求處理方法:
boolean setReplication(final String src, final short replication)
throws IOException {
final String operationName = "setReplication";
boolean success = false;
checkOperation(OperationCategory.WRITE);
final FSPermissionChecker pc = getPermissionChecker();
FSPermissionChecker.setOperationType(operationName);
try {
writeLock();
// 1)執行設置副本具體操作
try {
checkOperation(OperationCategory.WRITE);
checkNameNodeSafeMode("Cannot set replication for " + src);
success = FSDirAttrOp.setReplication(dir, pc, blockManager, src,
replication);
} finally {
writeUnlock(operationName, getLockReportInfoSupplier(src));
}
} catch (AccessControlException e) {
logAuditEvent(false, operationName, src);
throw e;
}
if (success) {
// 3)如果執行成功,執行logSync操作,寫出editlog到JN中
getEditLog().logSync();
logAuditEvent(true, operationName, src);
}
return success;
}
上面的內部setReplication方法:
static boolean setReplication(
FSDirectory fsd, FSPermissionChecker pc, BlockManager bm, String src,
final short replication) throws IOException {
bm.verifyReplication(src, replication, null);
final boolean isFile;
fsd.writeLock();
try {
final INodesInPath iip = fsd.resolvePath(pc, src, DirOp.WRITE);
if (fsd.isPermissionEnabled()) {
fsd.checkPathAccess(pc, iip, FsAction.WRITE);
}
final BlockInfo[] blocks = unprotectedSetReplication(fsd, iip,
replication);
isFile = blocks != null;
if (isFile) {
// 2)執行到此處,setReplication操作成功,寫出setReplication對應的editlog信息
fsd.getEditLog().logSetReplication(iip.getPath(), replication);
}
} finally {
fsd.writeUnlock();
}
return isFile;
}
基於RPC Call延時返回的異步editlog機制
瞭解完上述同步方式寫editlog的RPC Call調用後,下面我們再來看看異步editlog它是怎麼做的呢?
首先異步editlog要保證一個大的原則前提
Client收到的請求處理結果必須還是可靠的。
這裏要解決的真正難題在於我們既想讓editlog能夠被異步寫出,另外一方面Client調用線程的返回結果又得依賴於editlog的寫出完成,後者其實是依賴於前面的執行結果的。所以這裏我們的editlog的異步寫出並不是完全意義上的異步化。
異步editlog的核心改進在於它把logSync這種editlog同步寫出的比較重的操作從RPC Call處理方法中挪出去了,由另外一個線程做editlog的寫出操作了。取而代之的是,logSync就做一個簡單的editlog的進queue操作。這樣的話,NameNode server端的Handler馬上能take over處理別的請求了。隨後等待消費editlog的真正被寫出好後,同步editlog的線程再觸發返回client response的操作,這個時候client纔會收到請求處理的結果了。在這個過程中,我們還是保證了只要editlog寫出成功才返回這樣一個大的前提的。
簡單來說,對於server端內部來說,它的editlog寫出是異步執行,但對於client side而言,它的結果還是需要等待editlog的完成的。
這個過程的簡單圖示如下所示:
上圖顯示了2段虛線,第一段小的虛線指的是Handler線程處理執行logSync方法後,將待寫出的editlog加入pending editlog queue後,Handler就算處理請求完成了,隨後它可以繼續處理別的請求。第二段大虛線指當editlog寫出線程真正執行完logSync後,才觸發response的返回。
因此從這裏我們可以看到,RPC Call的延時返回策略起到的作用方式主要如下:
- 拆分原RPC Call操作中潛在的比較固定的而且可能比較重的操作到另外一個線程中處理,以此確保Handler線程能夠快速地執行完主要操作方法,以此增大Server的請求throughput。
- 對於異步線程執行前面拆分出來的操作,通過延時返回結果的方式讓client等待對於請求操作的執行完成,以此確保數據處理的準確性。
關於延時返回的改動,感興趣的同學參考社區JIRA:HADOOP-10300:Allowed deferred sending of call responses。當然本文所闡述的異步editlog只是使用延時回覆機制的一個使用的場景case,我們還可以有其它類似的適用場景。
相關鏈接
[1].https://issues.apache.org/jira/browse/HADOOP-10300
[2].https://blog.csdn.net/Androidlushangderen/article/details/106316751