BlockManagerMaster
上一篇博客簡單講解了BlockManager的運行機制,BlockManager在創建的時候首先會向BlockManagerMaster進行註冊,下面我們來看源碼中,在BlockManagerMaster上是如何註冊的:
register註冊函數
/**
* 註冊BlockManager
*/
private def register(id: BlockManagerId, maxMemSize: Long, slaveEndpoint: RpcEndpointRef) {
val time = System.currentTimeMillis()
// 首先判斷,如果本地HashMap中沒有指定的BlockManagerId,說明還沒有註冊過
// 先去註冊當前這個BlockManager
if (!blockManagerInfo.contains(id)) {
// 根據BlockManager對應的executorID找到對應的BlockManagerInfo
// 這裏做了一個安全判斷,如果BlockManagerInfo map裏。沒有BlockManagerId
// 那麼BlockManagerIdByExecutor map裏也應該沒有。如果有的話,那麼需要做一下清理
blockManagerIdByExecutor.get(id.executorId) match {
case Some(oldId) =>
// A block manager of the same executor already exists, so remove it (assumed dead)
logError("Got two different block manager registrations on same executor - "
+ s" will replace old one $oldId with new one $id")
// 從內存中,移除executorId相關的BlockManagerInfo
removeExecutor(id.executorId)
case None =>
}
logInfo("Registering block manager %s with %s RAM, %s".format(
id.hostPort, Utils.bytesToString(maxMemSize), id))
// 往blockManagerIdByExecutor map中保持一份executorId到blockManagerId的映射
blockManagerIdByExecutor(id.executorId) = id
// 爲當前BlockManagerId創建一份BlockManagerInfo,並往blockManagerInfo map中保存一份
// BlockManagerId到BlockManagerInfo的映射
blockManagerInfo(id) = new BlockManagerInfo(
id, System.currentTimeMillis(), maxMemSize, slaveEndpoint)
}
listenerBus.post(SparkListenerBlockManagerAdded(time, id, maxMemSize))
}
這個函數是在BlockManagerMasterEndpoint組件中,這個組件維護BlockManagerMaster與BlockManager的通信。
首先判斷當前BlockManager是否註冊過,如果還沒有註冊,那麼再做一個安全判斷,根據BlockManager對應的executorID找到對應的BlockManagerInfo,看能否找到,假如有的話,就移除executor相關的BlockManagerInfo(這裏就是看之前註冊的executorId中是否有相同的BlockMangerId);接着就開始註冊信息,往blockManagerIdByExecutor map中保持一份executorId到blockManagerId的映射,併爲當前BlockManagerId創建一份BlockManagerInfo,並往BlockManagerInfo map中保存一份BlockManagerId到BlockManagerInfo的映射,到這裏註冊就結束。
UpdateBlockInfo更新Block信息
接着我們看一下更新BlockInfo,也即如果每個BlockManager上Block發生了變換,那麼都要發送UpdateBlockInfo請求到BlockManagerMaster,進行BlockInfo的更新。我們看BlockManagerMaster接收到更新消息如何處理,代碼如下:
private def updateBlockInfo(
blockManagerId: BlockManagerId,
blockId: BlockId,
storageLevel: StorageLevel,
memSize: Long,
diskSize: Long,
externalBlockStoreSize: Long): Boolean = {
// 如果不包含這個BlockManagerId,並且是在Driver端,則啥也不做返回
if (!blockManagerInfo.contains(blockManagerId)) {
if (blockManagerId.isDriver && !isLocal) {
// We intentionally do not register the master (except in local mode),
// so we should not indicate failure.
return true
} else {
return false
}
}
// 如果是空,更新一下時間
if (blockId == null) {
blockManagerInfo(blockManagerId).updateLastSeenMs()
return true
}
// 調用updateBlockInfo對block信息進行更新
blockManagerInfo(blockManagerId).updateBlockInfo(
blockId, storageLevel, memSize, diskSize, externalBlockStoreSize)
// 這裏每一個Block可能會在多個BlockManager上
// 如果將StorageLevel設置爲 _2 這種格式,就需要將Block replicate一份到其他BlockManager上
// blockLocations map保存了每個BlockId對應的BlockManagerId的set集合
// 這裏會更新blockLocations中的信息,使用set集合自動去重
var locations: mutable.HashSet[BlockManagerId] = null
if (blockLocations.containsKey(blockId)) {
locations = blockLocations.get(blockId)
} else {
locations = new mutable.HashSet[BlockManagerId]
blockLocations.put(blockId, locations)
}
// 只對持久化級別是 Disk、Memory和OffHeap三種情況,進行添加
if (storageLevel.isValid) {
locations.add(blockManagerId)
} else {
locations.remove(blockManagerId)
}
// Remove the block from master tracking if it has been removed on all slaves.
// 如果各個節點上的block都被刪除了,那麼也清楚Master上相應的Block的信息
if (locations.size == 0) {
blockLocations.remove(blockId)
}
true
}
簡單分析一下,就是對BlockManager的信息進行更新,調用BlockManagerInfo的updateBlockInfo對Block信息進行更新,這裏更新主要是給每個BlockId創建一份BlockStatus,根據持久化級別(Disk、Memory或OffHeap),進行計算,更新到BlockManagerInfo map中;然後就是假設Block持久化級別是 _2 級別的(比如MEMORY_AND_DISK_2),就需要將Block replicate一份到其他Block上去,blockLocations map保存了每個BlockId對應的BlockManagerId的集合(可能不在一個節點上),這裏就是更新blockLocations的信息,使用set集合去重BlockManagerId。最後就是進行一些刪除操作。
簡單總結一下,BlockManager信息註冊比較重要的兩個一個數註冊register,一個是updateBlockInfo;其中第一個就是依據BlockManagerId生成一個BlockManagerInfo並進行保存;第二個就是更新BlockManagerInfo中的BlockStatus。(附上比較重要的組件的定義)
// 這個Map、映射了block manager id到block manager的info
// BlockManagerId - BlockManagerInfo的映射
// BlockManagerMaster要負責維護每個BlockManager的BlockManagerInfo
private val blockManagerInfo = new mutable.HashMap[BlockManagerId, BlockManagerInfo]
// 映射每個executor ID - BlockManagerId
private val blockManagerIdByExecutor = new mutable.HashMap[String, BlockManagerId]
// BlockManagerInfo管理了每個BlockManager內部的BlockId - BlockStatus
private val _blocks = new JHashMap[BlockId, BlockStatus]