Hadoop3.2.1 【 YARN 】原始碼分析 :RPC通訊解析

張伯毅發表於2020-12-07

一.前言

遠端過程呼叫(Remote Procedure Call, RPC) 是一個計算機通訊協議。 該協議允許執行於一臺計算機的程式呼叫另一臺計算機的子程式, 同時將網路的通訊細節隱藏起來,而程式設計師無需額外地為這個互動作用程式設計。

作為一個分散式系統, Hadoop實現了自己的RPC通訊協議, 它是上層多個分散式子系統(MR、 YARN、 HDFS等) 的公用網路通訊模組, 保證其輕量級、 高效能。

在這裡插入圖片描述

典型的RPC框架, 主要包括以下幾個部分。
( 1) 通訊模組: 主要是請求-應答協議, 包括同步方式和非同步方式。
①Stub程式: 客戶端和服務端均包含Stub程式, 可將之看作代理程式。
②排程程式: 排程程式接收來自通訊模組的請求訊息, 並根據其中的標識選擇一個Stub程式進行處理。
( 2) 客戶程式/服務過程: 請求的發出者和請求的處理者。
請求過程為: 客戶端程式—>客戶端Stub程式—>通訊模組—>遠端請求—>通訊模組—排程程式—伺服器Stub程式—>服務程式

二.RPC流程 概略圖

在這裡插入圖片描述

客戶端:這裡我們假設ConnectionId對應的Connection並不存在。在呼叫getConnection方法時,這裡構造了Connection,由於入參ConnectionId.doPing一般為true,因此,在Connection的構造方法時,會構造相應的pingHeader寫入到成員變數pingRequest中。接著將call加入到connection中後,呼叫了connection.setupIOstreams,這裡一開始就呼叫了writeConnectionHeader,一共寫了7個位元組的內容到服務端(分別是"hrpc",Version、Service Class、AuthProtocol,顯然,前面是四個位元組,後面三個都是一個位元組)。另外,由於這裡的成員變數doPing為true,因此,這裡使用PingInputStream封裝了上面獲取的輸入流。該方法內接著呼叫了writeConnectionContext,該方法就是rpc的認證呼叫。這裡的callId設定為CONNECTION_CONTEXT_CALL_ID,這裡最後呼叫了request.write,將request中的資訊寫到了out中,注意,這裡並沒有呼叫其flush方法。但是,這裡封裝了一次請求。接著,呼叫了connection.sendRpcRequest,這裡將請求的call繼續寫入到out中,並呼叫了flush方法。至此,客戶端的流程也就完成了。

服務端:關於服務端的流程,我從Server.Listener.run講起。這裡一開始是阻塞的。當客戶端呼叫了flush之後,這裡的阻塞被打斷。這裡然後呼叫到了doAccept方法。在該方法中首先呼叫到了ConnectionManager.register,這裡新構造了Connection並且將其加入到成員結合connections中。在Connection的構造中封裝了客戶端的channel、socket等相關資訊。然後將新構造的connection作為attachment繫結到SelectionKey上,然後呼叫reader.addConnection將其加入到阻塞佇列pendingConnections,並呼叫wakeup喚醒了reader在doRunLoop方法中的readSelector.select阻塞。而Server.Listener中的方法繼續阻塞。

接下來,我們重點關注Server.Listener.Reader.doRunLoop。這裡的readSelector.select阻塞打斷後,由於此時的readSelector中並沒有相關的selectedKeys,因此繼續迴圈執行。接著從pendingConnections佇列中取出封裝了客戶端相關資訊的Connection,然後將Connection.channel註冊到readSelector選擇器上,由於客戶端已經將資訊flush過來,所以,這裡的註冊後的readSelector不會阻塞,且其中含有相應的selectedKeys,故,接下來會呼叫到doRead方法,接著呼叫到了Connection.readAndProcess。接下來另起一段著重講解。

這裡dataLengthBuffer.remaining()>0判斷成立,呼叫channelRead將客戶端的版本資訊讀入。接著將Version、Service Class、AuthProtocol讀入到connectionHeaderBuf中,然後呼叫了dataLengthBuffer.flip,方便下次讀取資料長度,並且將connectionHeaderRead置為true,也就是下一次遍歷的時候不會再讀取頭部的相關資訊。只是讀取實際資料長度。然後,由於data == null,因此,這裡為data分配空間,然後呼叫了processOneRpc,由於這裡是封裝的CONNECTION_CONTEXT_CALL_ID的call,因此,這裡來到processRpcOutOfBandRequest方法,該方法完成了認證的相關流程。由於一開始connectionContextRead為false,而在processRpcOutOfBandRequest方法中被置為true,因此,這裡會呼叫continue。這裡重新讀取的資料的長度,然後繼續呼叫了processOneRpc。這一次callId不小於0,因此會呼叫到方法processRpcRequest。該方法是完成真正請求的,也就是說,前面的幾次呼叫只是前期的校驗。所謂真正的請求,也就是要呼叫服務端的相關方法。processRpcRequest方法將客戶端的相關資訊封裝到Call,並將其加入到callQueue中。

另一方面,在Handler.run中一直在等待callQueue中成員的加入。通過其take方法可以看到內部的阻塞佇列一直在輪詢等待成員的加入。這裡接著呼叫了CurCall.set(call),方便後面獲取客戶端的ugi。接著就呼叫到了RPC.Server.call,由於這裡的Server繼承自org.apache.hadoop.ipc.Server(該類是一個抽象類),而前面的Server覆寫了後面Server的抽象方法call。最終呼叫了ProtobufRpcEngine.cal,該方法完成了服務端對應方法的呼叫,並且將結果返回。

另外,如果客戶端在請求超時之後,會呼叫sendPing方法,用於測試服務端的服務是否仍然開啟,此時的callId為PING_CALL_ID。

三.YARN RPC的主要組成協議

在這裡插入圖片描述

(1) ApplicationClientProtocol: clients與RM之間的協議, JobClient通過該RPC協議提交應用程式、 查詢應用程式狀態等。
(2) ResourceManagerAdministrationProtocol: Admin與RM之間的通訊協議, Admin通過該RPC協議更新系統配置檔案, 例如節點黑白名單等。
(3) ApplicationMasterProtocol: AM與RM之間的協議, AM通過該RPC協議向RM註冊和撤銷自己, 併為各個任務申請資源。
(4) ContainerManagementProtocol: AM與NM之間的協議, AM通過該RPC要求NM啟動或者停止Container, 獲取各個Container的使用狀態等資訊。
(5) ResourceTracker: NM與RM之間的協議, NM通過該RPC協議向RM註冊, 並定時傳送心跳資訊彙報當前節點的資源使用情況和Container執行情況。

四.ApplicationClientProtocol

clients與RM之間的協議, JobClient通過該RPC協議提交應用程式、 查詢應用程式狀態、 叢集狀態、 節點、佇列和許可權控制等。

  • 應用資訊
方法名稱描述
getNewApplicationclient獲得一個單調遞增的ApplicationId用來提交Application .
響應資訊中會包含叢集的一些詳細資訊,比如叢集中指定最大資源能力
submitApplicationclient向ResourceManager提交Application.
客戶端需要通過SubmitApplicationRequest提供諸如佇列、執行ApplicationMaster所需的資源、用於啟動ApplicationMaster的容器的資料量等詳細資訊
ResourceManager如果接受請求的話會立即返回一個<空的>SubmitApplicationResponse,如果拒絕則直接回丟擲一個異常,然而 請求需要根據getApplicationReport方法確保application被正確的提交, 從ResourceManager獲取SubmitApplicationResponse並不保證RM在故障轉移或重新啟動之後“記住”此應用程式。
如果ResourceManager在成功儲存application之前發生了故障轉義或者重啟,getApplicationReport方法將會丟擲一個ApplicationNotFoundException異常.當clinet在通過getApplicationReport獲得一個ApplicationNotFoundException時,會以相同的ApplicationSubmissionContext配置重新提交application.
在提交 的過程中,會檢查application 是否存在,如果application存在,將會返回SubmitApplicationResponse.
在安全模式下,ResouceManager會在接受應用程式提交之前驗證對佇列等的訪問許可權。
failApplicationAttemptclient用來請求ResourceManager使應用程式嘗試失敗
forceKillApplicationclient請求ResourceManager終止已提交的Application
moveApplicationAcrossQueues將一個application移動到另外一個佇列
updateApplicationPriority更新application的優先順序

資源預約

方法名稱描述
getNewReservationclient請求ResourceManager獲取ReservationId
submitReservation提交reservation
updateReservation更新reservation
deleteReservation刪除reservation
listReservations根據篩選條件查詢reservation列表資訊.
可通過queue, reservationId, startTime,endTime,includeReservationAllocations等引數.
signalToContainer客戶端用於請求ResourceManager向容器發出訊號的介面
比如像container傳送OUTPUT_THREAD_DUMP命令獲取執行緒快照資訊.
安全模式下,ResourceManager在application傳送命令給container之前會驗證許可權資訊. 使用者需要具有<MODIFY_APP>許可權
updateApplicationTimeouts更新application的超時時間. 需要提供<yyyy-MM-dd’T’HH:mm:ss.SSSZ>格式時間,如果超時時間小於等於當前時間會丟擲異常

在這裡插入圖片描述

  1. 使用者提交預訂(reservation)建立請求,並接收包含ReservationId的響應。
  2. 使用者提交一個用RDL(Reservation Definition Language)表示的規格檔案和ReservationId組成的預訂請求。這描述了使用者對資源隨時間的需求(例如,資源的閾值)和時間約束(例如,期限)。這可以通過常用的Client-to-RM協議或通過RM的REST api以程式設計方式完成。如果使用相同的ReservationId提交預訂,並且RDL相同,則不會建立新預留並且請求將成功。如果RDL不同,則將拒絕預留,並且請求將不成功。如果使用相同的ReservationId提交預訂,並且RDL相同,則不會建立新預留但是請求將會成功。如果RDL不同,則將拒絕預留,並且請求將會不成功。
  3. ReservationSystem利用ReservationAgent(圖中的GREE)啟動一個客戶端去查詢合理的資源分配。一般是通過一個計劃,計劃是一個資料結構,描述了當前已經接受的保留和可獲得的資源。
  4. SharingPolicy提供一種方式去接受或者拒絕預訂。
  5. 成功驗證後,ReservationSystem會向使用者返回ReservationId。
    6.到一定時間,一個名為PlanFollower的新元件通過動態的建立/調整/銷燬佇列,將計劃狀態釋出到排程程式。
  6. 使用者可以提交一個作業到可預留佇列,可以明確指定ReservationId作為ApplicationSubmissionContext的一部分。
  7. Scheduler將從建立的特殊佇列中提供容器,以確保遵守資源預留。在預留的限制內,使用者已經保證訪問資源,在資源共享之上進行標準的Capacity/Fairness共享。
  8. 系統同時提供一個機制去適應叢集容量下降的機制。
  • 叢集/節點/配置資訊
方法名稱描述
getClusterMetricsclient 向ResourceManager獲取叢集資訊
getClusterNodesclient 向ResourceManager獲取叢集所有節點的資訊
getNodeToLabels獲取node節點的label資訊
getLabelsToNodes獲取label的node 資訊
getClusterNodeLabels獲取叢集中的label資訊
getResourceProfiles獲取特定資源配置[resource-profiles.json,]
getResourceTypeInfo獲取特定資源配置
getAttributesToNodes獲取attribute->node屬性
getClusterNodeAttributes獲取叢集node屬性
getNodesToAttributes獲取叢集node->attribute屬性
  • 佇列資訊
方法名稱描述
getQueueInfo獲取叢集佇列資訊[已使用/總容量/子佇列/正在執行任務資訊]
getQueueUserAclsClient從ResourceManager獲取有關當前使用者的佇列ACL的資訊

五.ResourceManagerAdministrationProtocol

Admin與RM之間的通訊協議, Admin通過該RPC協議更新系統配置檔案, 例如節點黑白名單等。ResourceManagerAdministrationProtocol繼成了GetUserMappingsProtocol協議介面.
介面資訊如下:
在這裡插入圖片描述

方法名稱描述
refreshQueues重新整理佇列
refreshNodes重新整理節點
refreshSuperUserGroupsConfiguration重新整理配置
refreshUserToGroupsMappings重新整理使用者->使用者組對映資訊
refreshAdminAcls重新整理Admin的ACL資訊
refreshServiceAcls重新整理服務級別資訊(SLA)
updateNodeResource更新在RM端維護的RMNode資源資訊
refreshNodesResources重新整理node資源資訊
addToClusterNodeLabels向叢集中節點新增Label
removeFromClusterNodeLabels移除叢集中節點Label
replaceLabelsOnNode替換叢集中節點Label
checkForDecommissioningNodes檢查停用的節點
refreshClusterMaxPriority重新整理群集最大優先順序
mapAttributesToNodes獲取attribute -> node 資訊

六.ResourceTracker

NM與RM之間的協議, NM通過該RPC協議向RM註冊, 並定時傳送心跳資訊彙報當前節點的資源使用情況和Container執行情況 [ 服務端ResourceTrackerService : 8031埠]。

方法名稱描述
nodeHeartbeat心跳資訊
registerNodeManager註冊NodeManager
unRegisterNodeManager解除註冊NodeManager

七.ApplicationMasterProtocol

AM與RM之間的協議, AM通過該RPC協議向RM註冊和撤銷自己, 併為各個任務申請資源。

方法名稱描述
registerApplicationMaster新的ApplicationMaster向RM註冊
ApplicationMaster會提供RPC埠,url等資訊給RM,響應資訊會返回叢集所能響應的最大資源能力
finishApplicationMasterApplicationMaster通知RM自己的狀態為成功/失敗
allocateApplicationMaster向ResourceManager申請資源/心跳

八.ContainerManagementProtocol

ContainerManagementProtocol: AM與NM之間的協議, AM通過該RPC要求NM啟動或者停止Container, 獲取各個Container的使用狀態等資訊。

方法名稱描述
startContainers啟動容器
stopContainers停止容器
getContainerStatuses獲取容器狀態
increaseContainersResource [廢棄]增加容器資源
updateContainer更新容器
signalToContainer傳送訊號
localize本地化容器所需的資源,目前,此API僅適用於執行容器
reInitializeContainer使用新的 Launch Context 初始化容器
restartContainer重新啟動容器
rollbackLastReInitialization嘗試回滾最後一次重新初始化操作
commitLastReInitialization嘗試提交最後一次初始化操作,如果提交成功則不可以回滾.

相關文章