Spark-SparkSession.Builder 源码解析

Spark-SparkSession.Builder 源码解析

  • class Builder
  • SparkSessionExtensions

class Builder

这个类主要用来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
    }

SparkSessionExtensions

注意这个类是**实验性质的*,主要的目的是 注入一些 扩展点(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已经阅读过了。

你可能感兴趣的:(Spark,源码,spark,scala)