SparkEnv是创建SparkContext中的第一步,也是很重要的一环,它Spark 的执行环境对象,其中包括众多与Executor 执行相关的对象。
由于在local 模式下Driver 会创建Executor,cluster 部署模式或者Standalone 部署模式下Worker 另起的CoarseGrainedExecutorBackend 进程中也会创建Executor,所以SparkEnv 存在于Driver 或者CoarseGrainedExecutorBackend 进程中,创建SparkEnv 主要使用SparkEnv类 的createDriverEnv,
private[spark] def createSparkEnv(
conf: SparkConf,
isLocal: Boolean,
listenerBus: LiveListenerBus): SparkEnv = {
SparkEnv.createDriverEnv(conf, isLocal, listenerBus, SparkContext.numDriverCores(master))
}
可以看到,传入的参数包括listerBus,所以在创建SparkEnv之前,要先创建JobProgressListener(application运行信息记录器)。
_jobProgressListener = new JobProgressListener(_conf)
listenerBus.addListener(jobProgressListener)
其实,createDriverEnv()最终调用的还是SparkEnv.create()创建SparkEnv。SparkEnv的构造步骤如下。
1. 创建安全管理器SecurityManager;
2. 创建Map 任务输出跟踪器mapOutputTracker;
3. 实例化ShuffleManager;
4. 创建MemoryManager;
5. 创建块传输服务BlockTransferService;
6. 创建BlockManagerMaster;
7. 创建块管理器BlockManager;
8. 创建广播管理器BroadcastManager;
9. 创建缓存管理器CacheManager;
10. 创建测量系统MetricsSystem;
11. 创建SparkEnv。
安全管理器SecurityManager
SecurityManager 主要对权限、账号进行设置,如果使用Hadoop YARN 作为集群管理器,则需要使用证书生成 secret key 登录,最后给当前系统设置默认的口令认证实例,此实例采用匿名内部类实现
/*
使用http 设置认证口令
*/
if (authOn) {
Authenticator.setDefault(
new Authenticator() {
override def getPasswordAuthentication(): PasswordAuthentication = {
var passAuth: PasswordAuthentication = null
val userInfo = getRequestingURL().getUserInfo()
if (userInfo != null) {
val parts = userInfo.split(":", 2)
passAuth = new PasswordAuthentication(parts(0), parts(1).toCharArray())
}
return passAuth
}
}
)
}
map 任务输出跟踪器mapOutputTracker
mapOutputTracker 用于跟踪map 阶段任务的输出状态,此状态便于reduce 阶段任务获取地址及中间输出结果。每个map 任务或者reduce 任务都会有其唯一标识,分别为mapId 和reduceId。每个reduce 任务的输入可能是多个map 任务的输出,reduce 会到各个map 任务的所在节点上拉取Block,这一过程叫做shuffle。每批shuffle 过程都有唯一的标识shuffleId。
这里先介绍下MapOutputTrackerMaster。MapOutputTrackerMaster 内部使用mapStatuses:TimeStampedHashMap[Int, Array[MapStatus]] 来维护跟踪各个map 任务的输出状态。其中key对应shuffleId,Array 存储各个map 任务对应的状态信息MapStatus。由于MapStatus 维护了map 输出Block 的地址BlockManagerId,所以reduce 任务知道从何处获取map 任务的中间输出MapOutputTrackerMaster 还使用cachedSerializedStatuses :TimeStampedHashMap[Int,Array[Byte]] 维护序列化后的各个map 任务的输出状态。其中key 对应shuffleId,Array 存储各个序列化MapStatus 生成的字节数组。
/**
* Timestamp based HashMap for storing mapStatuses and cached serialized statuses in the driver,
* so that statuses are dropped only by explicit de-registering or by TTL-based cleaning (if set).
* Other than these two scenarios, nothing should be dropped from this HashMap.
*/
protected val mapStatuses = new TimeStampedHashMap[Int, Array[MapStatus]]()
private val cachedSerializedStatuses = new TimeStampedHashMap[Int, Array[Byte]]()
Driver 和Executor 处理MapOutputTrackerMaster 的方式有所不同。
- 如 果 当 前 应 用 程 序 是 Driver, 则 创 建 MapOutputTrackerMaster, 然 后 创建MapOutputTrackerMasterEndpoint,并注册到rpcEnv中。
- 如果当前应用程序是 Executor,则创建 MapOutputTrackerWorker,并从 rpcEnv中找到MapOutputTrackerMasterEndpoint。
无论是Driver 还是Executor, 最后都由mapOutputTracker 的属性trackerEndpoint持有
MapOutputTrackerMasterActor 的引用RpcEndpointRef。
def registerOrLookupEndpoint(
name: String, endpointCreator: => RpcEndpoint):
RpcEndpointRef = {
if (isDriver) {
logInfo("Registering " + name)
rpcEnv.setupEndpoint(name, endpointCreator)
} else {
RpcUtils.makeDriverRef(name, conf, rpcEnv)
}
}
val mapOutputTracker = if (isDriver) {
new MapOutputTrackerMaster(conf)
} else {
new MapOutputTrackerWorker(conf)
}
mapOutputTracker.trackerEndpoint = registerOrLookupEndpoint(MapOutputTracker.ENDPOINT_NAME,
new MapOutputTrackerMasterEndpoint(
rpcEnv, mapOutputTracker.asInstanceOf[MapOutputTrackerMaster], conf))
map 任务的状态正是由Executor 向持有的MapOutputTrackerMasterEndpoint发送消息, 将map 任务状态同步到mapOutputTracker 的mapStatuses 和cachedSerializedStatuses 的。
实例化ShuffleManager
ShuffleManager 负责管理本地及远程的block 数据的shuffle 操作。ShuffleManager默认为通过反射方式生成的SortShuffleManager 的实例, 可以修改属性spark.shuffle.manager 为hash 来显式控制使用HashShuffleManager
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)
内存管理器MemoryManager
在Spark-1.6.0中,引入了一个新的参数spark.memory.userLegacyMode(默认值为false),表示不使用Spark-1.6.0之前的内存管理机制,而是使用1.6.0中引入的动态内存分配这一概念,主要影响到构造memoryManager的类的不同。
val useLegacyMemoryManager = conf.getBoolean("spark.memory.useLegacyMode", false)
val memoryManager: MemoryManager =
if (useLegacyMemoryManager) {
new StaticMemoryManager(conf, numUsableCores)
} else {
UnifiedMemoryManager(conf, numUsableCores)
}
StaticMemoryManager是Spark-1.6.0之前版本中主要使用的,对各部分内存静态划分好后便不可变化。
而useLegacyMemoryManager ,根据其注释看一看到。
该memoryManager主要是使得execution部分和storage部分的内存不像之前由比例参数限定住,而是两者可以互相借用内存。execution和storage总的内存上限由参数`spark.memory.fraction(默认0.75)来设定的,这个比例是相对于整个JVM heap来说的。
Storage部分可以申请Execution部分的所有空闲内存,直到Execution内存不足时向Storage发出信号为止。当Execution需要更多内存时,Storage部分会向磁盘spill数据,直到把借用的内存都还上为止。
同样的Execution部分也能向Storage部分借用内存,当Storage需要内存时,Execution中的数据不会马上spill到磁盘,因为Execution使用的内存发生在计算过程中,如果数据丢失就会到账task计算失败。Storage部分只能等待Execution部分主动释放占用的内存。
块传输服务BlockTransferService
BlockTransferService 默认为NettyBlockTransferService,它使用Netty 提供的异步事件驱动的网络
应用框架,提供web 服务及客户端,获取远程节点上Block 的集合。
val blockTransferService = new NettyBlockTransferService(conf, securityManager, numUsableCores)
BlockManagerMaster
BlockManagerMaster 负责对Block 的管理和协调, 具体操作依赖于BlockManagerMasterActor。Driver 和Executor 处理BlockManagerMaster 的方式不同:
val blockManagerMaster = new BlockManagerMaster(registerOrLookupEndpoint(
BlockManagerMaster.DRIVER_ENDPOINT_NAME,
new BlockManagerMasterEndpoint(rpcEnv, isLocal, conf, listenerBus)),
conf, isDriver)
与mapOutputTracker类似。
- 如果当前应用程序是Driver,则创建 BlockManagerMasterRpcEndPoint,并且注册到 RpcEnv中
- 如果当前应用程序是 Executor,则从 RpcEnv中找到 BlockManagerMasterRpcEndPoint
无论是Driver 还是Executor, 最后BlockManagerMaster 的属性driverEndPoint 将持有对
BlockManagerMasterRpcEndPoint 的引用RpcEndpointRef。
class BlockManagerMaster(
var driverEndpoint: RpcEndpointRef,
conf: SparkConf,
isDriver: Boolean)
extends Logging {
创建块管理器BlockManager
BlockManager 负责对Block 的管理, 是存储系统的一部分只有在BlockManager 的初始化方法initialize
被调用后,它才是有效的。
// 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)
创建广播管理器BroadcastManager
BroadcastManager 用于将配置信息和序列化后的RDD、Job 以及ShuffleDependency 等信息
在本地存储。如果为了容灾,也会复制到其他节点上。
val broadcastManager = new BroadcastManager(isDriver, conf, securityManager)
BroadcastManager 必须在其初始化方法initialize 被调用后,才能生效。initialize 方法实际利用反射生成广播工厂实例broadcastFactory(可以配置属性spark.broadcast.factory 指定, 默认org.apache.spark.broadcast.TorrentBroadcastFactory)。BroadcastManager 的广播方法newBroadcast 实际代理了工厂broadcastFactory 的newBroadcast 方法来生成广播对象。unbroadcast 方法实际代理了工厂broadcastFactory 的unbroadcast 方法生成非广播对象。
private def initialize() {
synchronized {
if (!initialized) {
val broadcastFactoryClass =
conf.get("spark.broadcast.factory", "org.apache.spark.broadcast.TorrentBroadcastFactory")
broadcastFactory =
Utils.classForName(broadcastFactoryClass).newInstance.asInstanceOf[BroadcastFactory]
// Initialize appropriate BroadcastFactory and BroadcastObject
broadcastFactory.initialize(isDriver, conf, securityManager)
initialized = true
}
}
}
def newBroadcast[T: ClassTag](value_ : T, isLocal: Boolean): Broadcast[T] = {
broadcastFactory.newBroadcast[T](value_, isLocal, nextBroadcastId.getAndIncrement())
}
def unbroadcast(id: Long, removeFromDriver: Boolean, blocking: Boolean) {
broadcastFactory.unbroadcast(id, removeFromDriver, blocking)
}
创建缓存管理器CacheManager
CacheManager 用于缓存RDD 某个分区计算后的中间结果,缓存计算结果发生在迭代计算的时候。
测量系统MetricsSystem
val metricsSystem = if (isDriver) {
// Don't start metrics system right now for Driver.
// We need to wait for the task scheduler to give us an app ID.
// Then we can start the metrics system.
MetricsSystem.createMetricsSystem("driver", conf, securityManager)
} else {
// We need to set the executor ID before the MetricsSystem is created because sources and
// sinks specified in the metrics configuration file will want to incorporate this executor
// ID into the metrics they report.
conf.set("spark.executor.id", executorId)
val ms = MetricsSystem.createMetricsSystem("executor", conf, securityManager)
ms.start()
ms
}
构造MetricsSystem 的过程最重要的是调用了MetricsConfig 的initialize 方法.
def initialize() {
// Add default properties in case there's no properties file
setDefaultProperties(properties)
loadPropertiesFromFile(conf.getOption("spark.metrics.conf"))
// Also look for the properties in provided Spark configuration
val prefix = "spark.metrics.conf."
conf.getAll.foreach {
case (k, v) if k.startsWith(prefix) =>
properties.setProperty(k.substring(prefix.length()), v)
case _ =>
}
propertyCategories = subProperties(properties, INSTANCE_REGEX)
if (propertyCategories.contains(DEFAULT_PREFIX)) {
val defaultProperty = propertyCategories(DEFAULT_PREFIX).asScala
for((inst, prop) <- propertyCategories if (inst != DEFAULT_PREFIX);
(k, v) <- defaultProperty if (prop.get(k) == null)) {
prop.put(k, v)
}
}
}
创建SparkEnv
当所有的基础组件准备好后,开始创建SparkEnv
val envInstance = new SparkEnv(
executorId,
rpcEnv,
actorSystem,
serializer,
closureSerializer,
cacheManager,
mapOutputTracker,
shuffleManager,
broadcastManager,
blockTransferService,
blockManager,
securityManager,
sparkFilesDir,
metricsSystem,
memoryManager,
outputCommitCoordinator,
conf)
之所以并没有介绍actorSystem,是因为在spark1.6中已经把actorSystem移除了。
_actorSystem: ActorSystem, // TODO Remove actorSystem
至此,SparkEnv创建完毕!