spark BlockManager如何实现Broadcast广播

当通过SparkContext调用broadcast()方法的时候,将会直接尝试调用BroadcastFactory的newBroadcast()方法,BroadcastFactory的默认实现是TorrentBroadcastFactory,在其newBroadcast()方法中,实际上就是new了一个TorrentBroadcast。

在TorrentBroadcast的初试化流程中,将会通过writeBlocks()方法准备将需要广播的变量写入到BlockManager中。

val blockManager = SparkEnv.get.blockManager
if (!blockManager.putSingle(broadcastId, value, MEMORY_AND_DISK, tellMaster = false)) {
  throw new SparkException(s"Failed to store $broadcastId in BlockManager")
}

在广播变量的持久化中,调用了BlockManager的putSingle()方法,来将广播变量写到磁盘上,这里是对广播对象在driver端的一个备份,并不是后续提供给executor的。

val blocks =
  TorrentBroadcast.blockifyObject(value, blockSize, SparkEnv.get.serializer, compressionCodec)
if (checksumEnabled) {
  checksums = new Array[Int](blocks.length)
}
blocks.zipWithIndex.foreach { case (block, i) =>
  if (checksumEnabled) {
    checksums(i) = calcChecksum(block)
  }
  val pieceId = BroadcastBlockId(id, "piece" + i)
  val bytes = new ChunkedByteBuffer(block.duplicate())
  if (!blockManager.putBytes(pieceId, bytes, MEMORY_AND_DISK_SER, tellMaster = true)) {
    throw new SparkException(s"Failed to store $pieceId of $broadcastId in local BlockManager")
  }
}

 

在写完全部广播对象后,将会将其根据配置的单份文件的大小进行切割。将要广播的对象通过BlockManager的putBytes()方法,切割成一个个小文件,持久化到磁盘上,两者在底层都是通过DiskBlockManager的getFile()方法,将blockid进行hash的结果定位到子目录上创建文件并写入具体的数据。

 

在driver端的广播过程中,主要就是通过BlockManager将要广播的对象,切割成小文件持久化到了磁盘上。其中putBytes()与putSingle()的区别在于,一个直接提供已经序列化好了的bytes流,一个直接提供对象来进行序列化。

 

当广播变量在driver端完成了变量的序列化,在executor将会通过广播变量的value()方法获取具体的广播对象。

在TorrentBroadcast的value()方法中,最后的实际调用实则是readBroadcastBlock()方法。

在readBroadcastBlock()方法中,首先根据广播变量对应的broadcastid寻找本地是否已经存在这份广播变量,如果已经存在,会直接返回内存中对这份文件的缓存,否则,将会通过readBlocks()尝试远程拉取远程的广播变量。

在readBlocks()中,TorrentBroadcast的实现,将会从各个持有小分片的远程BlockManager通过BlockTransferService进行远程广播数据的拉取,在拉取之前,将会向driver端的BlockManagerMaster获取一个持有小文件的所有BlockManager,随机选择一个进行拉取,保证文件分片后的高效拉取。具体的获取方式底层BlockManager的调用方式与shuffle获取一样,还是通过netty获取远端的数据拉取。

在获取到所有小文件之后,在TorrentBroadcast中进行合并,缓存到内存中,并返回,已达到后续使用的时候可以高效返回。

你可能感兴趣的:(spark)