这个类主要用来builder SparkSession
有以下的属性:
1.private[this] val options = new scala.collection.mutable.HashMap[String, String];用来存放SparkConf 传递过来的config和在builder对象是设置的cofig
2.private[this] val extensions = new SparkSessionExtensions这个类的说明见下面,注意这个类是实验性质的。
3.private[this] var userSuppliedContext: Option[SparkContext] = None;用来存放用户提供的SparkContext,有可能用户自己先 实例化了一个 SparkContext,可以通过 这个类的 sparkContext方法(这个方法只限在spark内部使用)暂时保存 用户的sparkContext,最后在这个类的 getOrCreate 方法中 进行兼容处理。
阅读一下这个类的方法:
//暂时保存 用户的sparkContext spark 内部使用
//例如 SQLContext 中 this(SparkSession.builder().sparkContext(sc).getOrCreate())
//用来创建SQLContext实例
private[spark] def sparkContext(sparkContext: SparkContext): Builder = synchronized {
userSuppliedContext = Option(sparkContext)
this
}
//设置config到这个类的 options 中去保存,还有各种value其他类型的重载方法
def config(key: String, value: String): Builder = synchronized {
options += key -> value
this
}
//设置 spark.app.name
def appName(name: String): Builder = config("spark.app.name", name)
//把用户在sparkConf中设置的config 加到 这个类的 options 中去
def config(conf: SparkConf): Builder = synchronized {
conf.getAll.foreach { case (k, v) => options += k -> v }
this
}
//设置 spark.master 可以是 local、lcoal[*]、local[int]
def master(master: String): Builder = config("spark.master", master)
//用来检查是否可以支持连接hive 元数据,支持集成hive
def enableHiveSupport(): Builder = synchronized {
//hiveClassesArePresent 是SparkSession Object的一个方法,用来判断是否包含
//hive的一些支持包(org.apache.spark.sql.hive.HiveSessionStateBuilder,org.apache.hadoop.hive.conf.HiveConf),
//通过ClassForName 反射来判定所需的jar是否存在,自已这里的ClassForName 是spark自己封装的,目的在于尽可能使用启动本线程的 类加载器
//如果所需的集成hive依赖都在的话,hiveClassesArePresent 会返回true
if (hiveClassesArePresent) {
//在此类Builder的属性options中加入
//catalog的config (spark.sql.catalogImplementation, hive)
config(CATALOG_IMPLEMENTATION.key, "hive")
} else {
throw new IllegalArgumentException(
"Unable to instantiate SparkSession with Hive support because " +
"Hive classes are not found.")
}
}
//这个方法用来增加扩展点(injection points|extension points)到
//SparkSessionExtensions里面
def withExtensions(f: SparkSessionExtensions => Unit): Builder = synchronized {
f(extensions)
this
}
注意,如果要兼容hive,则需要依赖 spark-hive maven依赖和hive-exec maven依赖
下面把最重要的方法getOrCreate单独列出来讲解:
//这个方法是 持有对象锁的
def getOrCreate(): SparkSession = synchronized {
//判断先行状态,以test模型运行spark(如果在System.getenv() 中配置了SPARK_TESTING或者SystemProperties配置了spark.testing)
//和判断当前线程变量中存在taskContext(taskContext: ThreadLocal[TaskContext] = new ThreadLocal[TaskContext] 表示,有这个task运行) 则会抛出异常
assertOnDriver()
// Get the session from current thread's active session.
//activeThreadSession 这个也是一个 线程变量,如果父线程这个线程变量是null的话 第一次 getOrCreate的时候这个一定是null
var session = activeThreadSession.get()
if ((session ne null) && !session.sparkContext.isStopped) {
options.foreach { case (k, v) => session.sessionState.conf.setConfString(k, v) }
if (options.nonEmpty) {
logWarning("Using an existing SparkSession; some configuration may not take effect.")
}
return session
}
// Global synchronization so we will only set the default session once.
//进入到类锁 范围
SparkSession.synchronized {
// If the current thread does not have an active session, get it from the global session.
//第一次为 null
session = defaultSession.get()
if ((session ne null) && !session.sparkContext.isStopped) {
options.foreach { case (k, v) => session.sessionState.conf.setConfString(k, v) }
if (options.nonEmpty) {
logWarning("Using an existing SparkSession; some configuration may not take effect.")
}
return session
}
// No active nor global default session. Create a new one.
//如果 这类的属性userSuppliedContext 没有值,
//则会重新 创建一个sparkConf 并把自己属性options 所有保存的 config 都set到新
//的sparkConf,如果用户自己没事设置 appName的话,那么就会生成一个随机的appName
//得到sparkConText
val sparkContext = userSuppliedContext.getOrElse {
val sparkConf = new SparkConf()
options.foreach { case (k, v) => sparkConf.set(k, v) }
// set a random app name if not given.
if (!sparkConf.contains("spark.app.name")) {
sparkConf.setAppName(java.util.UUID.randomUUID().toString)
}
//得到sparkConText 这个在另外一篇在详细讲解。
SparkContext.getOrCreate(sparkConf)
// Do not update `SparkConf` for existing `SparkContext`, as it's shared by all sessions.
}
// Initialize extensions if the user has defined a configurator class.
//如果用户自己配置spark的扩展(配置config: spark.sql.extensions-》自己的实现类的全路径)
//则会加载自己的扩展类
val extensionConfOption = sparkContext.conf.get(StaticSQLConf.SPARK_SESSION_EXTENSIONS)
if (extensionConfOption.isDefined) {
val extensionConfClassName = extensionConfOption.get
try {
val extensionConfClass = Utils.classForName(extensionConfClassName)
val extensionConf = extensionConfClass.newInstance()
.asInstanceOf[SparkSessionExtensions => Unit]
extensionConf(extensions)
} catch {
// Ignore the error if we cannot find the class or when the class has the wrong type.
case e @ (_: ClassCastException |
_: ClassNotFoundException |
_: NoClassDefFoundError) =>
logWarning(s"Cannot use $extensionConfClassName to configure session extensions.", e)
}
}
//实例化SparkSession,并把此类保存在options中的所有的config 依次设置到sparkSession的initialSessionOptions的map中
//最后把实例化后的SparkSession设置dao Spark Object的 defaultSession和activeThreadSession中去,以便下次使用
session = new SparkSession(sparkContext, None, None, extensions)
options.foreach { case (k, v) => session.initialSessionOptions.put(k, v) }
setDefaultSession(session)
setActiveSession(session)
// Register a successfully instantiated context to the singleton. This should be at the
// end of the class definition so that the singleton is updated only if there is no
// exception in the construction of the instance.
sparkContext.addSparkListener(new SparkListener {
override def onApplicationEnd(applicationEnd: SparkListenerApplicationEnd): Unit = {
defaultSession.set(null)
}
})
}
return session
}
注意这个类是**实验性质的*,主要的目的是 注入一些 扩展点(injection points|extension points)。例如:
1.Analyzer Rules
2.Check Analysis Rules
3.Optimizer Rules
4.Planning Strategies
5.Customized Parser
6.(External) Catalog listeners
使用方式(要通过Builder来组合进来):
SparkSession.builder()
.master("...")
.conf("...", true)
.withExtensions { extensions =>
extensions.injectResolutionRule { session =>
...
}
extensions.injectParser { (session, parser) =>
...
}
}
.getOrCreate()
注意这个类的方法参数很多都是函数类型的,例如:
type RuleBuilder = SparkSession => Rule[LogicalPlan]
type CheckRuleBuilder = SparkSession => LogicalPlan => Unit
type StrategyBuilder = SparkSession => Strategy
type ParserBuilder = (SparkSession, ParserInterface) => ParserInterface
//这个RuleBuilder就是上面的type RuleBuilder 可以看出他是一个函数类型的参数声明
//所以在上面的使用例子中,里面都是写的 匿名函数
def injectResolutionRule(builder: RuleBuilder): Unit = {
resolutionRuleBuilders += builder
}
这里总结一下,由Builder源码又引出了4个类或Object,SparkSession、SparkConf、SparkContext、StaticSQLConf;
SparkConf和StaticSQLConf已经阅读过了。