Spark — BlockManagerMaster信息註冊

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]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章