概述
shuffle过程研究基于本地运行模式的LocalScheduler;ShuffleFetcher实现为SimpleShuffleFetcher。
涉及组件
shuffle过程涉及的组件:
- MapOutputTracker
- ShuffleManager
- ShuffleFetcher
shuffle文件生成在ShuffleMapTask
类型run
方法中,shuffle数据fetch在依赖类型为宽依赖的rdd(如ShuffledRDD、CoGroupedRDD)的compute
方法中。
各组件功能
todo:分别有多少个实例,分布于何处
ShuffleManager
- 生成shuffle文件存放路径
- 创建shuffle文件
- 启动shuffle文件访问server
- 维护所在server的shuffle文件访问路径
MapOutputTracker
- 维护所有shuffle过程的shuffle文件访问路径(以shuffleId为key的ConcurrentHashMap)
- 根据自身角色,维护用于通信的trackerActor(用scala的异步编程scala.actors实现)
- master启动DaemonActor的实现:MapOutputTrackerActor,进行所有shuffle的shuffle文件访问路径维护,响应slaver的MapOutputTrackerActor的数据请求
- slaver启动RemoteActor,向master的MapOutputTrackerActor获取所需shuffleId的shuffle文件路径,并维护在自己的内存中
ShuffleFetcher
- http访问获取shuffle文件
- 根据传入的计算逻辑在fetch方法中聚合fetch到的数据
shuffle标识
具有宽依赖的rdd创建ShuffleDependency
对象时,会调用SparkContext.newShuffleId
的为该宽依赖分配一个唯一的shuffleId(SparkContext中用一个AtomicInteger维护)作为一个shuffle过程的唯一标识。
shuffle 实现
执行过程
- 在划分stage阶段,在生成stage时会将该stage宽依赖的shuffle注册到MapOutputTracker,并为该shuffle生成shuffle文件的访问路径列表存储分配空间:
def registerShuffle(shuffleId: Int, numMaps: Int) {
if (serverUris.get(shuffleId) != null) {
throw new IllegalArgumentException("Shuffle ID " + shuffleId + " registered twice")
}
serverUris.put(shuffleId, new Array[String](numMaps))
}
-
ShuffleMapTask执行run方法(Task抽象类run方法的具体实现),生成shuffle文件;run方法返回shuffle文件访问路径(提供文件访问的httpserver地址)。在此过程中,ShuffleManager负责创建shuffle文件、初始化提供shuffle文件访问的httpserver、返回shuffle文件访问地址。
-
shuffle文件访问路径被返回到master存入master的MapOutputTracker。
-
依赖类型为宽依赖的rdd执行compute方法进行shuffle的fetch操作。fetch的实际执行由ShuffleFetcher进行。ShuffleFetcher调用
MapOutputTracker.getServerUris
方法获取该shuffle map端的shuffle文件server地址。同时根据shuffleId和reduceId拼装出所需shuffle文件路径,连接并读取数据。
详细讲解shuffle过程的具体实现:
Mapper端功能实现
从构造函数可以看出,一个ShuffleMapTask执行一个rdd的某个split的计算工作:
class ShuffleMapTask(
runId: Int,
stageId: Int,
rdd: RDD[_], // 负责计算的rdd
dep: ShuffleDependency[_,_,_],
val partition: Int, // 负责计算的split
locs: Seq[