阅读指导:在《Spark2.1.0——SparkContext概述》一文中,曾经简单介绍了SparkEnv。本节内容将详细介绍SparkEnv的创建过程。
在Spark中,凡是需要执行任务的地方就需要SparkEnv。在生产环境中,SparkEnv往往运行于不同节点的Executor中。但是由于local模式在本地执行的需要,因此在Driver本地的Executor也需要SparkEnv。SparkContext中创建SparkEnv的实现见代码清单1。
代码清单1 创建SparkEnv
private[spark] val listenerBus = new LiveListenerBus(this)
_jobProgressListener = new JobProgressListener(_conf)
listenerBus.addListener(jobProgressListener)
_env = createSparkEnv(_conf, isLocal, listenerBus)
SparkEnv.set(_env)
因为SparkEnv内的很多组件都将向LiveListenerBus的事件队列中投递事件,所以在代码清单1中首先创建LiveListenerBus和JobProgressListener,然后将JobProgressListener添加到LiveListenerBus的监听器列表中,最后将LiveListenerBus通过SparkEnv的构造器传递给SparkEnv以及SparkEnv内部的组件。JobProgressListener继承自SparkListener,LiveListenerBus和SparkListener的详细内容已在《Spark2.1.0之源码分析——事件总线》及其系列文章中介绍,此处不再赘述。
createDriverEnv方法用于创建SparkEnv,根据createDriverEnv这个方法名,我们知道此方法将为Driver实例创建SparkEnv。调用createSparkEnv方法创建完SparkEnv后,SparkEnv实例的引用将通过SparkEnv的set方法设置到SparkEnv伴生对象[1]的env属性中,类似于设置为Java类的静态属性,这将便于在任何需要SparkEnv的地方,通过伴生对象的get方法获取SparkEnv。
createSparkEnv方法创建SparkEnv的代码如下:
private[spark] def createSparkEnv(
conf: SparkConf,
isLocal: Boolean,
listenerBus: LiveListenerBus): SparkEnv = {
SparkEnv.createDriverEnv(conf, isLocal, listenerBus, SparkContext.numDriverCores(master))
}
可以看到实际调用了SparkEnv的createDriverEnv方法来创建SparkEnv。SparkEnv的createDriverEnv方法的实现如下:
private[spark] def createDriverEnv(
conf: SparkConf,
isLocal: Boolean,
listenerBus: LiveListenerBus,
numCores: Int,
mockOutputCommitCoordinator: Option[OutputCommitCoordinator] = None): SparkEnv = {
assert(conf.contains(DRIVER_HOST_ADDRESS),
s"${DRIVER_HOST_ADDRESS.key} is not set on the driver!")
assert(conf.contains("spark.driver.port"), "spark.driver.port is not set on the driver!")
val bindAddress = conf.get(DRIVER_BIND_ADDRESS)
val advertiseAddress = conf.get(DRIVER_HOST_ADDRESS)
val port = conf.get("spark.driver.port").toInt
val ioEncryptionKey = if (conf.get(IO_ENCRYPTION_ENABLED)) {
Some(CryptoStreamUtils.createKey(conf))
} else {
None
}
create(
conf,
SparkContext.DRIVER_IDENTIFIER,
bindAddress,
advertiseAddress,
port,
isLocal,
numCores,
ioEncryptionKey,
listenerBus = listenerBus,
mockOutputCommitCoordinator = mockOutputCommitCoordinator
)
}
createDriverEnv方法首先从SparkConf中获取4个属性。
private[spark] val DRIVER_HOST_ADDRESS = ConfigBuilder("spark.driver.host")
.doc("Address of driver endpoints.")
.stringConf
.createWithDefault(Utils.localHostName())
private[spark] val DRIVER_BIND_ADDRESS = ConfigBuilder("spark.driver.bindAddress")
.doc("Address where to bind network listen sockets on the driver.")
.fallbackConf(DRIVER_HOST_ADDRESS)
根据DRIVER_BIND_ADDRESS的定义,说明按照优先级从高到低,可以通过spark.driver.bindAddress属性、spark.driver.host属性以及调用Utils的localHostName方法获得bindAddress。Utils的localHostName方法的实现请参阅附录A。
真正创建SparkEnv的实现都在create方法中,由于SparkEnv是Driver和Executor实例中都有的组件,本书将在第5章对SparkEnv作详细介绍。
[1] 什么是伴生对象?这是Scala语言的语法特性。Scala中的伴生对象可以提供类似于Java中的静态方法、静态属性等的定义,也可以支持类似于Java中的main方法。更多内容请读者阅读Scala语言相关资料。