我们Spark的代码开头往往是这样的
val conf: SparkConf = new SparkConf().setAppName("app").setMaster("local[2]")
根据代码和官方的注释可以知道,SparkConf是Spark应用程序的配置类,通常用来设置KV结构的Spark参数。它还具有以下基础的特性。
new SparkConf()
时,大多数情况下会把Java参数(resource)中带有Spark.*
格式的参数自动设置到应用中。但是,你通过set方法直接设置的属性优先级会高于Java参数(resource)。new SparkConf(false)
的方式来跳过加载系统参数的过程。class SparkConf(loadDefaults: Boolean) extends Cloneable with Logging with Serializable {
import SparkConf._
/** Create a SparkConf that loads defaults from system properties and the classpath */
def this() = this(true)
private val settings = new ConcurrentHashMap[String, String]()
//......
}
首先是import SparkConf._
,它引入的伴生对象有如下方法
其次是settings
,它是一个线程安全的Map来作为Spark配置项的容器。
@transient private lazy val reader: ConfigReader = {
val _reader = new ConfigReader(new SparkConfigProvider(settings))
_reader.bindEnv((key: String) => Option(getenv(key)))
_reader
}
}
reader
是个懒加载的方法,它最后返回的是一个ConfigReader
对象,这个对象作用是帮助读取配置参数和参数格式的替换。读取的参数按照前缀分为三种:
ConfigReader
对象时,传入了一个SparkConfigReader
对象,这个对象封装了前面提到的Spark参数容器settings
,它就作为无前缀配置项让ConfigReader
读取这个类构造了一个Map类型的属性bindings作为以上三种参数的容器,类里也有对应参数类型的bind方法将参数注入到bindings中,最后统一替换bindings的格式。
参数替换使用了一个正则表达式将参数替换成prefix:name
的形式。
最终返回的reader将在运行时由其他类通过相应get方法获取配置参数。
private[spark] def get[T](entry: ConfigEntry[T]): T = {
entry.readFrom(reader)
}
if (loadDefaults) {
loadFromSystemProperties(false)
}
private[spark] def loadFromSystemProperties(silent: Boolean): SparkConf = {
// Load any spark.* system properties
for ((key, value) <- Utils.getSystemProperties if key.startsWith("spark.")) {
set(key, value, silent)
}
this
}
其次是loadFromSystemProperties
,就是开头提到的加载Java参数,类的唯一一个参数loadDefaults就是控制是否加载Java参数。
方法内部遍历spark.
开头的参数然后由set方法注入到setttings
中。
private[spark] def set(key: String, value: String, silent: Boolean): SparkConf = {
if (key == null) {
throw new NullPointerException("null key")
}
if (value == null) {
throw new NullPointerException("null value for " + key)
}
if (!silent) {
logDeprecationWarning(key)
}
settings.put(key, value)
this
}
set方法不允许key或value为空,否则会抛出空指针异常,silent属性判断参数是否过期,最后放入settings
中。返回this可以让我们链式调用set方法,即文章开头示例代码。
除此以外,常用的setMaster,setAppName
以及其他的setXX
方最后调用的都是上面的基础set方法。
def getOption(key: String): Option[String] = {
Option(settings.get(key)).orElse(getDeprecatedConfig(key, settings))
}
将settings
获取到的值作为Option返回,如果settings
没有相应的key则会通过伴生对象的方法查找是否是过期的配置项。
其他封装的get方法例如带有默认值的get,获取各种类型的get等最终调用的都是getOption
。
源码过长就不贴了,这个方法会校验某些特殊参数并打印warn,还会校验JVM参数和内存参数的合法性,逻辑较为简单。
SparkConf
就是用于各种参数的获取和配置,并最后作为参数传入到SparkContext
中,SparkContext
是程序总入口,它接受了SparkConf
定义的配置参数后开始初始化工作,然后运行程序。