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]