我是跟着源码点进去一步一写的,所以观看时希望大家能一步一跟着源码走,不要只看博文。
在SparkContext 284行创建SparkEnv:
// This function allows components created by SparkEnv to be mocked in unit tests:
private[spark] def createSparkEnv(
conf: SparkConf,
isLocal: Boolean,
listenerBus: LiveListenerBus): SparkEnv = {
SparkEnv.createDriverEnv(conf, isLocal, listenerBus, SparkContext.numDriverCores(master))
}
SparkEnv.createDriverEnv最终会调用SparkEnv 233行create:
/**
* Helper method to create a SparkEnv for a driver or an executor.
*/
private def create(
conf: SparkConf,
executorId: String,
hostname: String,
port: Int,
isDriver: Boolean,
isLocal: Boolean,
numUsableCores: Int,
listenerBus: LiveListenerBus = null,
mockOutputCommitCoordinator: Option[OutputCommitCoordinator] = None): SparkEnv = {
该方法会构造如下几个重要的成员:
①分布式消息系统RpcEnv
②mapOutputTracker
③ShuffleManager
④BlockManager
一一解答:
2,RpcEnv构建
个人认为,如果把分布式系统(HDFS, HBASE,SPARK等)比作一个人,那么RPC可以认为是人体的血液循环系统。它将系统中各个不同的组件(如Hbase中的master, Regionserver, client)联系了起来。同样,在spark中,不同组件像driver,executor,worker,master(stanalone模式)之间的通信也是基于RPC来实现的。
Spark 1.6之前,spark的RPC是基于Akaa来实现的。Akka是一个基于scala语言的异步的消息框架。Spark1.6后,spark借鉴Akka的设计自己实现了一个基于Netty的rpc框架。大概的原因是1.6之前,RPC通过Akka来实现,而大文件是基于netty来实现的,加之akka版本兼容性问题,所以1.6之后把Akka改掉了,具体jira见(https://issues.apache.org/jira/browse/SPARK-5293)。
本文主要对spark1.6之后基于netty新开发的rpc框架做一个较为深入的分析。
2.1整体架构
spark 基于netty新的rpc框架借鉴了Akka的中的设计,它是基于Actor模型,各个组件可以认为是一个个独立的实体,各个实体之间通过消息来进行通信。具体各个组件之间的关系图如下(图片来自[1]):
2.2 RpcEndpoint
表示一个个需要通信的个体(如master,worker,driver),主要根据接收的消息来进行对应的处理。一个RpcEndpoint经历的过程依次是:构建->onStart→receive→onStop。其中onStart在接收任务消息前调用,receive和receiveAndReply分别用来接收另一个RpcEndpoint(也可以是本身)send和ask过来的消息。
2.3 RpcEndpointRef
RpcEndpointRef是对远程RpcEndpoint的一个引用。当我们需要向一个具体的RpcEndpoint发送消息时,一般我们需要获取到该RpcEndpoint的引用,然后通过该应用发送消息。
2.4 RpcAddress
表示远程的RpcEndpointRef的地址,Host + Port。
2.5 RpcEnv
RpcEnv为RpcEndpoint提供处理消息的环境。RpcEnv负责RpcEndpoint整个生命周期的管理,包括:注册endpoint,endpoint之间消息的路由,以及停止endpoint。
SparkEnv 253行create创建RpcEnv:
val rpcEnv = RpcEnv.create(actorSystemName, hostname, port, conf, securityManager,
clientMode = !isDriver)
调用RpcEnv 53行getRpcEnvFactory:
getRpcEnvFactory(conf).create(config)
解读:同过RpcEnvFactory创建RpcEnv
RpcEnv 35行getRpcEnvFactory具体实现:
private def getRpcEnvFactory(conf: SparkConf): RpcEnvFactory = {
val rpcEnvNames = Map(
"akka" -> "org.apache.spark.rpc.akka.AkkaRpcEnvFactory",
"netty" -> "org.apache.spark.rpc.netty.NettyRpcEnvFactory")
val rpcEnvName = conf.get("spark.rpc", "netty")
val rpcEnvFactoryClassName = rpcEnvNames.getOrElse(rpcEnvName.toLowerCase, rpcEnvName)
Utils.classForName(rpcEnvFactoryClassName).newInstance().asInstanceOf[RpcEnvFactory]
}
解读:根据配置创建对应的rpc框架 ,默认为netty,通过反射创建RpcEnvFactory。
3, ActorySystem
SparkEnv 265行:
// Create a ActorSystem for legacy codes
AkkaUtils.createActorSystem(
actorSystemName + "ActorSystem",
hostname,
actorSystemPort,
conf,
securityManager
)._1
AkkaUtils 53行:
doCreateActorSystem(name, host, actualPort, conf, securityManager)
最终在AkkaUtiles 121行创建actorSystem:
val actorSystem = ActorSystem(name, akkaConf)
解析 :因为不是很重要就没有解读了。
4, mapOutputTracker创建
mapoutputTracker用于跟踪map的输出状态,便于reduce获取。每个map或者reduce任务都会有唯一任务id。每个reduce任务的输入可能有多个map输出,reduce会到各个map任务的节点拉取Block这个过程叫shuffle,每个shuffle过程都有一个唯一标识shuffleId。
SparkEnv 328行创建mapOutputTracker:
//根据isDriver创建不同的对象。
val mapOutputTracker = if (isDriver) {
new MapOutputTrackerMaster(conf)
} else {
new MapOutputTrackerWorker(conf)
}
先看MapOutputTrackerMaster。
MapOutputTrackerMaster 273行 构造器:
private[spark] class MapOutputTrackerMaster(conf: SparkConf)
extends MapOutputTracker(conf) {
比较重要的成员对象 MapOutputTracker 299行:mapStatuses,cachedSerializedStatuses
//key为shuffleId,Array中的MapStatus跟踪各个map任务的输出状态,Mapstatus中维护的了map输出
//block的地址BolckMangerId,所以reduce知道从何处获得map任务的输出状态。
protected val mapStatuses = new TimeStampedHashMap[Int, Array[MapStatus]]()
//维护了序列化后map任务的输出状态。
private val cachedSerializedStatuses = new TimeStampedHashMap[Int, Array[Byte]]()
MapOutputTrackerWorker:
/**
* MapOutputTracker for the executors, which fetches map output information from the driver's
* MapOutputTrackerMaster.
*/
//从driver端获取对应的map output信息
private[spark] class MapOutputTrackerWorker(conf: SparkConf) extends MapOutputTracker(conf) {
protected val mapStatuses: Map[Int, Array[MapStatus]] =
new ConcurrentHashMap[Int, Array[MapStatus]]().asScala
}
回到SparkEnv
SparkEnv 334行构建MapOutputTrackerMasterEndpoint并获取MapOutputTrackerMasterEndpoint引用:
// Have to assign trackerActor after initialization as MapOutputTrackerActor
// requires the MapOutputTracker itself
mapOutputTracker.trackerEndpoint = registerOrLookupEndpoint(MapOutputTracker.ENDPOINT_NAME,
new MapOutputTrackerMasterEndpoint(
rpcEnv, mapOutputTracker.asInstanceOf[MapOutputTrackerMaster], conf))
SparkEnv 317行registerOrLookupEndpoint具体实现:
def registerOrLookupEndpoint(
name: String, endpointCreator: => RpcEndpoint):
RpcEndpointRef = {
if (isDriver) {
logInfo("Registering " + name)
rpcEnv.setupEndpoint(name, endpointCreator)
} else {
RpcUtils.makeDriverRef(name, conf, rpcEnv)
}
}
解析:如果是Driver会创建MapOutputTrackerMasterEndpoint 然后获得MapOutputTrackerMasterEndpoint的引用RpcEndpointRef,
Executor直接获得MapOutputTrackerMasterEndpoint的引用RpcEndpointRef。
RpcEndpointRef可以理解为对象的引用。
map任务的状态是由Executor是向MapOutputTrackerMasterEndpoint发送消息来将map状态同步到MapStatus中的。registerOrLookupEndpoint获得的引用就是找到MapOutputTrackerMasterEndpoint的关键。
5, ShuffleManager
SparkEnv 341:
val shortShuffleMgrNames = Map(
"hash" -> "org.apache.spark.shuffle.hash.HashShuffleManager",
"sort" -> "org.apache.spark.shuffle.sort.SortShuffleManager",
"tungsten-sort" -> "org.apache.spark.shuffle.sort.SortShuffleManager")
val shuffleMgrName = conf.get("spark.shuffle.manager", "sort")
val shuffleMgrClass = shortShuffleMgrNames.getOrElse(shuffleMgrName.toLowerCase, shuffleMgrName)
val shuffleManager = instantiateClass[ShuffleManager](shuffleMgrClass)
解读:ShuffleManager负责管理本地和远程的shuffle操作的。使用反射的方式生成的(默认)SortShuffleManager。
6 BlockManger
SparkEnv 364:
// NB: blockManager is not valid until initialize() is called later.
val blockManager = new BlockManager(executorId, rpcEnv, blockManagerMaster,
serializer, conf, memoryManager, mapOutputTracker, shuffleManager,
blockTransferService, securityManager, numUsableCores)
解读:BlockManager负责对Block管理。
总结
创建了几个很重要的成员。
用于进行消息传递的RpcEnv
用于负责跟踪map输出的mapOutputTracker
另外两个以后的博客会讲