Spark原始碼解析之Storage模組

破棉襖發表於2015-11-17

Storage模組整體架構

Storage模組主要分為兩層:

  1. 通訊層:storage模組採用的是master-slave結構來實現通訊層,master(Driver)和slave(Executor)之間傳輸控制資訊、狀態資訊,這些都是透過通訊層來實現的。
  2. 儲存層:storage模組需要把資料儲存到disk或是memory上面,有可能還需replicate到遠端,這都是由儲存層來實現和提供相應介面。

而其他模組若要和storage模組進行互動,storage模組提供了統一的操作類BlockManager,外部類與storage模組打交道都需要透過呼叫BlockManager相應介面來實現。



理論不多說,貼程式碼以作備忘:

SparkContext類中的_env物件在初始化:
  1. private[spark] def createSparkEnv(
  2.       conf: SparkConf,
  3.       isLocal: Boolean,
  4.       listenerBus: LiveListenerBus): SparkEnv = {
  5.     SparkEnv.createDriverEnv(conf, isLocal, listenerBus) //建立一個SparkEnv 包含一個BlockManagerMaster物件(啟動BlockManagerMasterEndPoint並獲取Ref)
  6.   }

BlockManagerMaster有個Actor訊息傳遞模型BlockManagerMasterEndpoint,該物件用來跟蹤所有塊管理器的資訊,它有比較重要的三個變數:

1.blockManagerInfo:HashMap容器,key值為BlockManagerID物件,value值為Bolck資訊(即BlockManagerInfo物件),Block資訊包括BlockManagerID,最大記憶體,以及從節點上的actor模型;

2.blockManagerIdByExecutor:HashMap容器,key值存放ExecutorID,value值為對應的BlockManagerID物件;

3.blockLocations:JHashMap容器,key值存放相應的塊(BlockId物件),可能有多個塊管理器擁有該塊,所以value值就為管理該塊的所有的塊管理器所構成一個HashSet


Executor中會起一個BlockManagerSlaveEndpoint來和BlockManagerMasterEndpoint通訊,負責刪除塊等操作

看一下Executor:   

  1. if (!isLocal) {
  2.     env.metricsSystem.registerSource(executorSource)
  3.     env.blockManager.initialize(conf.getAppId)
  4.   }
blockManager中會向主節點註冊:
  1. def initialize(appId: String): Unit = {
  2.     blockTransferService.init(this)
  3.     shuffleClient.init(appId)

  4.     blockManagerId = BlockManagerId(
  5.       executorId, blockTransferService.hostName, blockTransferService.port)

  6.     shuffleServerId = if (externalShuffleServiceEnabled) {
  7.       logInfo(s"external shuffle service port = $externalShuffleServicePort")
  8.       BlockManagerId(executorId, blockTransferService.hostName, externalShuffleServicePort)
  9.     } else {
  10.       blockManagerId
  11.     }

  12.     master.registerBlockManager(blockManagerId, maxMemory, slaveEndpoint)     //向主節點註冊BlockManager

  13.     // Register Executors' configuration with the local shuffle service, if one should exist.
  14.     if (externalShuffleServiceEnabled && !blockManagerId.isDriver) {
  15.       registerWithExternalShuffleServer()
  16.     }
  17.   }

看一下BlockManagerMasterEndpoint:
  1. override def receiveAndReply(context: RpcCallContext): PartialFunction[Any, Unit] = {
  2.     case RegisterBlockManager(blockManagerId, maxMemSize, slaveEndpoint) =>
  3.       register(blockManagerId, maxMemSize, slaveEndpoint)
  4.       context.reply(true)
  5.      .....

  1. private def register(id: BlockManagerId, maxMemSize: Long, slaveEndpoint: RpcEndpointRef) {
  2.     val time = System.currentTimeMillis()
  3.     if (!blockManagerInfo.contains(id)) {
  4.       blockManagerIdByExecutor.get(id.executorId) match {
  5.         case Some(oldId) =>
  6.           // A block manager of the same executor already exists, so remove it (assumed dead)
  7.           logError("Got two different block manager registrations on same executor - "
  8.               + s" will replace old one $oldId with new one $id")
  9.           removeExecutor(id.executorId)
  10.         case None =>
  11.       }
  12.       logInfo("Registering block manager %s with %s RAM, %s".format(
  13.         id.hostPort, Utils.bytesToString(maxMemSize), id))

  14.       blockManagerIdByExecutor(id.executorId) = id

  15.       blockManagerInfo(id) = new BlockManagerInfo(                          //新增到blockManagerInfo
  16.         id, System.currentTimeMillis(), maxMemSize, slaveEndpoint)
  17.     }
  18.     listenerBus.post(SparkListenerBlockManagerAdded(time, id, maxMemSize))
  19.   }

Executor中的run方法中呼叫Task的run方法。Task的run方法呼叫實現類的runTask方法,runTask方法中呼叫Rdd的iterator迭代方法。
runTask方法返回MapStatus物件。該物件包含了該檔案儲存的BlockManagerId和不同ReduceId要讀取的資料大小。Executor中會將
資料序列化並根據大小決定是直接返回還是存入BlockManager。


未完待續....



參考


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29754888/viewspace-1839920/,如需轉載,請註明出處,否則將追究法律責任。

相關文章