Shuffle 与 Storage 模块间的交互
来源:互联网 发布:sql 镜像服务器 编辑:程序博客网 时间:2024/06/08 05:21
7.6 Shuffle 与 Storage 模块间的交互
在Spark中存储模块被抽象成Storage,顾名思义,Storage是存储的意思,代表着Spark中的数据存储系统,负责管理和实现数据块(Block)的存放。其中存取数据的最小单元是Block,数据由不同的Block组成,所有操作都是以Block为单位进行的。本质上讲RDD中的Partition和Storage中的Block是等价的,只是所处的模块不同看待的角度不一样而已。
Storage抽象模块的实现分为两个层次,如下图所示。
图 7- 13 Storage存储模块
1) 通信层:通信层是典型的Master-Slave结构,Master和Slave之间传输控制和状态信息。通信层主要由BlockManager、BlockManagerMaster、BlockManagerMasterEndpoint、BlockManagerSlaveEndpoint等类实现。
2) 存储层:负者把数据存储到内存、磁盘或者堆外内存中,有时还需要为数据在远程节点上生成副本,这些都由存储层提供的接口实现。具体的存储层的实现类有抽象类BlockStore,实现类DiskStore、MemoryStore、ExternalBlockStore等。
Shuffle模块若要和Storage模块进行交互,需要通过调用统一的操作类BlockManager来完成。如果把整个存储模块看成一个黑盒,BlockManager就是黑盒上留出的一个供外部调用的接口。
7.6.1 Shuffle 注册的交互
Spark中 BlockManager在Driver端的创建,在SparkContext创建的时候会根据具体的配置创建SparkEnv对象。源代码如下所示。
SparkContext.scala源码:
1. _env =createSparkEnv(_conf, isLocal, listenerBus)
2. SparkEnv.set(_env)
3. .......
4. private[spark] def createSparkEnv(
5. conf: SparkConf,
6. isLocal: Boolean,
7. listenerBus: LiveListenerBus): SparkEnv ={
8. //创建Driver端的运行环境
9. SparkEnv.createDriverEnv(conf, isLocal,listenerBus, SparkContext.numDriverCores(master))
10. }
createSparkEnv方法中,传入SparkConf配置对象、isLocal标志、以及LiveListenerBus,方法中使用SparkEnv对象的createDriverEnv方法创建SparkEnv并返回。在SparkEnv的createDriverEvn方法中,将会创建BlockManager、BlockManagerMaster等对象,完成Storage在Driver端的部署。
SparkEnv中创建BlockManager、BlockManagerMaster关键源代码如下所示。
SparkEnv.scala源码:
1. valblockTransferService =
2. new NettyBlockTransferService(conf,securityManager, bindAddress, advertiseAddress,
3. blockManagerPort, numUsableCores)
4. //创建BlockManagerMasterEndpoint
5. valblockManagerMaster = new BlockManagerMaster(registerOrLookupEndpoint(
6. BlockManagerMaster.DRIVER_ENDPOINT_NAME,
7. //创建BlockManagerMasterEndpoint
8. new BlockManagerMasterEndpoint(rpcEnv,isLocal, conf, listenerBus)),
9. conf, isDriver)
10. //创建BlockManager
11. //NB: blockManager is not valid until initialize() is called later.
12. valblockManager = new BlockManager(executorId, rpcEnv, blockManagerMaster,
13. serializerManager, conf, memoryManager, mapOutputTracker,shuffleManager,
14. blockTransferService, securityManager,numUsableCores)
使用new关键字实例化出BlockManagerMaster,传入BlockManager的构造函数,实例化出BlockManager对象。这里的BlockManagerMaster和BlockManager属于聚合关系。BlockManager主要对外提供统一的访问接口,BlockManagerMaster主要对内提供各节点之间的指令通信服务。
在构建BlockManager的,传入shuffleManager参数,shuffleManager是在SparkEnv中创建的,将shuffleManager传入到BlockManager中,BlockManager就拥有shuffleManager的成员变量,从而可以调用shuffleManager的相关方法。
SparkEnv.scala源码
1. valshortShuffleMgrNames = Map(
2. "sort" -> classOf[org.apache.spark.shuffle.sort.SortShuffleManager].getName,
3. "tungsten-sort" ->classOf[org.apache.spark.shuffle.sort.SortShuffleManager].getName)
4. valshuffleMgrName = conf.get("spark.shuffle.manager", "sort")
5. valshuffleMgrClass = shortShuffleMgrNames.getOrElse(shuffleMgrName.toLowerCase,shuffleMgrName)
6. valshuffleManager = instantiateClass[ShuffleManager](shuffleMgrClass)
BlockManagerMaster在Driver端和Executors中的创建稍有差别。首先来看在Driver端创建的情形。创建BlockManagerMaster传入的isDriver参数,isDriver为true表示在Driver端创建,否则视为在Slave节点上创建。
当SparkContext中执行_env.blockManager.initialize(_applicationId)代码时,会调用Driver端BlockManager的initialize方法。Initialize方法源代码如下所示。
SparkContext.scala源码:
1. _env.blockManager.initialize(_applicationId)
BlockManager.scala源码:
1. definitialize(appId: String): Unit = {
2. //调用blockTransferService的init方法,blockTransferService用于在不同节点fetch数据,传送数据
3. blockTransferService.init(this)
4. //shuffleClient用于读取其他Executor上的shuffle files
5. shuffleClient.init(appId)
6.
7. blockReplicationPolicy = {
8. val priorityClass = conf.get(
9. "spark.storage.replication.policy",classOf[RandomBlockReplicationPolicy].getName)
10. val clazz =Utils.classForName(priorityClass)
11. val ret =clazz.newInstance.asInstanceOf[BlockReplicationPolicy]
12. logInfo(s"Using $priorityClass forblock replication policy")
13. ret
14. }
15.
16. valid =
17. BlockManagerId(executorId,blockTransferService.hostName, blockTransferService.port, None)
18.
19. //向blockManagerMaster注册BlockManager。在registerBlockManager方法中传入了slaveEndpoint,slaveEndpoint为BlockManager中的RPC对象,用于和blockManagerMaster通信
20. validFromMaster = master.registerBlockManager(
21. id,
22. maxMemory,
23. slaveEndpoint)
24. //得到blockManagerId
25. blockManagerId = if (idFromMaster != null)idFromMaster else id
26.
27. //得到shuffleServerId
28. shuffleServerId = if(externalShuffleServiceEnabled) {
29. logInfo(s"external shuffle serviceport = $externalShuffleServicePort")
30. BlockManagerId(executorId,blockTransferService.hostName, externalShuffleServicePort)
31. }else {
32. blockManagerId
33. }
34. //注册shuffleServer
35. //Register Executors' configuration with the local shuffle service, if one shouldexist.
36. if(externalShuffleServiceEnabled && !blockManagerId.isDriver) {
37. registerWithExternalShuffleServer()
38. }
39.
40. logInfo(s"Initialized BlockManager:$blockManagerId")
41. }
如上面源代码所示,initialize方法使用appId初始化BlockManager。主要完成:
1) 初始化BlockTransferService。
2) 初始化ShuffleClient。
3) 创建BlockManagerId。
4) 将BlockManager注册到BlockManagerMaster上。
5) 若ShuffleService可用,注册ShuffleService。
在BlockManager的initialize方法上点击右键 Find Usages,可以看到initialize方法在两个地方得到调用,一个是SparkContext,另一个是Executor。在启动Executor时,会调用BlockManager的initialize方法。Executor中调用initialize方法源代码如下所示。
Executor.scala源码:
1. //CoarseGrainedExecutorBackend中实例化Executor,isLocal设置成false,即Executor中isLocal始终为fasle
2.
3. if(!isLocal) {
4. //向度量系统注册
5. env.metricsSystem.registerSource(executorSource)
6. //调用BlockManager的initialize方法, initialize方法将向BlockManagerMaster注册,完成Executor中的BlockManager向Driver中的BlockManager的注册
7. env.blockManager.initialize(conf.getAppId)
8. }
上面代码中,调用了env.blockManager.initialize方法。在initialize方法中,完成BlockManger向Master端BlockManagerMaster的注册。使用方法master.registerBlockManager(id,maxMemory,slaveEndpoint)完成注册,registerBlockManager方法中传入Id、maxMemory、salveEndPoint引用,分别表示Executor中的BlockManager、最大内存、BlockManger中的BlockMangarSlaveEndpoint。BlockManagerSlaveEndpoint是一个RPC端点,使用它完成同BlockManagerMaster的通信。BlockManager收到收到注册请求后将Executor中注册的BlockManagerInfo存入哈希表中,以便通过BlockManagerSlaveEndpoint向Executor发送控制命令。
ShuffleManager是一个用于shuffle系统的可插拔接口。在driver端SparkEnv中创建ShuffleManager创建,在每一个executor上也会创建。基于spark.shuffle.manager进行设置。driver 使用ShuffleManager注册到shuffles系统,executors(或driver在本地运行的任务)可以请求读取和写入数据。这将被SparkEnv的SparkConf和isDriver布尔值作为参数。
ShuffleManager.scala源码:
1. private[spark]trait ShuffleManager {
2.
3. /**
4. *Register a shuffle with the manager and obtain a handle for it to pass totasks.
5. */
6. defregisterShuffle[K, V, C](
7. shuffleId: Int,
8. numMaps: Int,
9. dependency: ShuffleDependency[K, V, C]):ShuffleHandle
10.
11. /**Get a writer for a given partition. Called on executors by map tasks. */
12. defgetWriter[K, V](handle: ShuffleHandle, mapId: Int, context: TaskContext):ShuffleWriter[K, V]
13.
14. /**
15. *Get a reader for a range of reduce partitions (startPartition toendPartition-1, inclusive).
16. *Called on executors by reduce tasks.
17. */
18. defgetReader[K, C](
19. handle: ShuffleHandle,
20. startPartition: Int,
21. endPartition: Int,
22. context: TaskContext): ShuffleReader[K,C]
23.
24. /**
25. *Remove a shuffle's metadata from the ShuffleManager.
26. *@return true if the metadata removed successfully, otherwise false.
27. */
28. defunregisterShuffle(shuffleId: Int): Boolean
29.
30. /**
31. * 返回一个能够根据块坐标来检索shuffle 块数据的解析器。
32. */
33. defshuffleBlockResolver: ShuffleBlockResolver
34.
35. /** 关闭ShuffleManager.*/
36. defstop(): Unit
37. }
Spark Shuffle Pluggable框架ShuffleBlockManager在Spark 1.6.0之后改成了ShuffleBlockResolver。 ShuffleBlockResolver具体读取shuffle数据,是一个trait。在ShuffleBlockResolver中已无getBytes方法。getBlockData(blockId:ShuffleBlockId)方法返回的是ManagedBuffer,这个是核心。
ShuffleBlockResolver源码:
1. trait ShuffleBlockResolver {
2. typeShuffleId = Int
3.
4. /**
5. *Retrieve the data for the specified block. If the data for that block is notavailable,
6. *throws an unspecified exception.
7. */
8. defgetBlockData(blockId: ShuffleBlockId): ManagedBuffer
9.
10. defstop(): Unit
11. }
spark 2.0版本中通过SortShuffleWriterIndexShuffleBlockResolver来具体实现ShuffleBlockResolver(SortBasedShuffl方式),已无FileShuffleBlockManager(Hashshuffle方式)。SortShuffleWriterIndexShuffleBlockResolver创建和维护逻辑块和物理文件位置之间的shuffle blocks映射关系。来自于相同map task任务的shuffle blocks数据存储在单个合并数据文件中;数据文件中的数据块的偏移量存储在单独的索引文件中。将 shuffleBlockId + reduce ID set to 0 + ".后缀" 作为数据shuffledata的shuffleBlockId名字。其中, 文件名后缀为".data"的是数据文件;文件名后缀为".index"的是索引文件。
7.6.2 Shuffle 写数据的交互
基于Sort的Shuffle实现的ShuffleHandle包含BypassMergeSortShuffleHandle与BaseShuffleHandle。两种ShuffleHandle写数据的方法可以参考SortShuffleManager类的getWriter方法,关键代码如下所示:
SortShuffleManager的getWriter源码:
1. override def getWriter[K, V](
2. …….
3. case bypassMergeSortHandle:BypassMergeSortShuffleHandle[K @unchecked, V @unchecked] =>
4. newBypassMergeSortShuffleWriter(
5. env.blockManager,
6. shuffleBlockResolver.asInstanceOf[IndexShuffleBlockResolver],
7. ……..
8. case other:BaseShuffleHandle[K @unchecked, V @unchecked, _] =>
9. new SortShuffleWriter(shuffleBlockResolver,other, mapId, context)
10. }
11. }
在对应构建的两种数据写入器类BypassMergeSortShuffleWriter与SortShuffleWriter中,都是通过变量shuffleBlockResolver对逻辑数据块与物理数据块的映射进行解析。 BypassMergeSortShuffleWriter写数据的具体实现位于实现的write方法中,其中调用的createTempShuffleBlock()方法描述了各个分区所生成的中间临时文件的格式与对应的BlockId;SortShuffleWriter写数据的具体实现位于实现的write方法中。
7.6.3 Shuffle 读数据的交互
SparkEnv.get.shuffleManager.getReader是SortShuffleManager的getReader,是获取数据的阅读器,getReader方法中创建了一个BlockStoreShuffleReader实例。BlockStoreShuffleReader.scala的read()方法源码:
1. override def getReader[K, C](
2. handle: ShuffleHandle,
3. startPartition: Int,
4. endPartition: Int,
5. context: TaskContext):ShuffleReader[K, C] = {
6. new BlockStoreShuffleReader(
7. handle.asInstanceOf[BaseShuffleHandle[K,_, C]], startPartition, endPartition, context)
8. }
BlockStoreShuffleReader实例的read()方法,首先实例化newShuffleBlockFetcherIterator。ShuffleBlockFetcherIterator是一个阅读器,里面有一个成员blockManager,blockManager是内存和磁盘上数据读写的统一管理器;ShuffleBlockFetcherIterator.scala的initialize方法中splitLocalRemoteBlocks()划分本地和远程的blocks, Utils.randomize(remoteRequests)把远程请求通过随机的方式添加到队列中, fetchUpToMaxBytes()发送远程请求获取我们的block,fetchLocalBlocks()获取本地的blocks。
- Shuffle 与 Storage 模块间的交互
- c语言write与python的struct模块交互
- Android Camera HAL3中拍照Capture模式下多模块间的交互与帧Result与帧数据回调
- Android Camera HAL3中拍照Capture模式下多模块间的交互与帧Result与帧数据回调
- Android Camera HAL3中拍照Capture模式下多模块间的交互与帧Result与帧数据回调
- Android Camera HAL3中拍照Capture模式下多模块间的交互与帧Result与帧数据回调
- 第六步:继续完善域结构并实现键盘模块与视频模块的交互
- Shuffle模块详解
- Storage模块详解
- 使用python的内置ctypes模块与c、c++写的dll进行交互
- Hadoop中的Shuffle 与 Spark中的Shuffle的区别与联系
- Shuffle的播放记录Shuffle
- 8 storage的tracker_merge_servers与文件同步
- fastdfs storage server的设计与实现
- 与storage目录有关的修改
- storage与cookie的相同点和区别
- 关于cookie与storage的一些理解
- IronPython 与 c# 交互之导入Python模块的两种方法
- Android一些开源第三方
- assets、raw(文件夹、作用,区别) 、 SharedPreference(存放配置信息)
- andrid studio导入java项目
- P1809【USACO2.3.1】Longest Prefix最长前缀 IOI'96
- JDBC使用技术及事例
- Shuffle 与 Storage 模块间的交互
- python pip 国内源
- AxonFramework,存储库
- 跟着Django手册建立了Blog(四)--写静态文件
- MSSQL:使用表格多页浏览数据库
- OL3和echart的结合
- 约瑟夫问题
- HTML+CSS编写静态网站-13 包含外部css样式
- 【机器学习算法模型】分类算法——人工神经网络