spark源码分析:(一)spark-shell启动脚本时候过程

当我们在命令行中输入spark-shell的时候,会自动转为spark shell界面。这个界面中我们可以完成spark的操作。那么,这个过程是怎么进行的呢?

当我们在命令行中输入spark-shell的时候,调用的是spark/bin/spark-shell脚本。以下是spark-shell脚本中的部分代码:

function main() {
  if $cygwin; then
    stty -icanonmin 1 -echo > /dev/null 2>&1
    export SPARK_SUBMIT_OPTS="$SPARK_SUBMIT_OPTS -Djline.terminal=unix"
    "$FWDIR"/bin/spark-submit --class org.apache.spark.repl.Main "${SUBMISSION_OPTS[@]}" spark-shell "${APPLICATION_OPTS[@]}"
sttyicanon echo > /dev/null 2>&1
  else
    export SPARK_SUBMIT_OPTS
    "$FWDIR"/bin/spark-submit --class org.apache.spark.repl.Main "${SUBMISSION_OPTS[@]}" spark-shell "${APPLICATION_OPTS[@]}"
fi
}

通过上方代码我们可以清楚的看见,main()方法调用了spark-submit脚本。我们前往spark-submit脚本去查看,发现spark-submit又调用了spark-class脚本。

exec "$SPARK_HOME"/bin/spark-class org.apache.spark.deploy.SparkSubmit "${ORIG_ARGS[@]}"

调用了spark-class脚本,并传入若干个参数,其中一个参数是org.apache.spark.deploy.SparkSubmit。spark-class脚本接收到传入的参数,并执行下述语句。

if [ -n "${JAVA_HOME}" ]; then
  RUNNER="${JAVA_HOME}/bin/java"
else
  if [ `command -v java` ]; then
    RUNNER="java"
  else
    echo "JAVA_HOME is not set" >&2
    exit 1
  fi
fi
 
exec "$RUNNER" -cp "$CLASSPATH" $JAVA_OPTS "$@"

上述语句的最后一条表示启动java程序(org.apache.spark.deploy.SparkSubmit)。这个时候,我们应该知道Spark启动了以SparkSubmit为主类的jvm进程。

上述是脚本之间的调用,那么进入spark shell环境时候,像系统自动生成的conf,sc又是怎么得到的呢?

通过使用jvisualvm工具来dump main线程的信息,信息显示如下:

"main" - Thread t@1
   java.lang.Thread.State: RUNNABLE
		...
        at org.apache.spark.repl.SparkILoop.process(SparkILoop.scala:916)
        at org.apache.spark.repl.SparkILoop.process(SparkILoop.scala:1011)
		...
        at org.apache.spark.repl.Main.main(Main.scala)
		...
        at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:75)
        at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
		...

从main线程的栈信息中看出程序的调用顺序:SparkSubmit.main→repl.Main→SparkILoop.process。SparkILoop.process方法中会调用initializeSpark方法,initializeSpark实现如下:

def initializeSpark() {
intp.beQuietDuring {
      command("""
         @transient val sc = {
           val _sc = org.apache.spark.repl.Main.interp.createSparkContext()
           println("Spark context available as sc.")
           _sc
         }
        """)
      command("import org.apache.spark.SparkContext._")
    }
  }

从上述代码中,我们可以发现sc的创建是通过createSparkContext()方法完成的。然后,输出“Spark context available as sc.”,并将sc返回,同时导入SparkContext包下的所有类或对象。我们再看下createSparkContext()方法,看看他的内部是怎么实现的。

def createSparkContext(): SparkContext = {
    val execUri = System.getenv("SPARK_EXECUTOR_URI")
    val jars = SparkILoop.getAddedJars
    val conf = new SparkConf()
          .setMaster(getMaster())
          .setAppName("Spark shell")
          .setJars(jars)
          .set("spark.repl.class.uri", intp.classServer.uri)
    if (execUri != null) {
        conf.set("spark.executor.uri", execUri)
    }
    sparkContext = new SparkContext(conf)
    logInfo("Created spark context..")
    sparkContext
  }

在这个方法中,我们创建了conf对象,并对conf做了一些配置,比如说setMaster(),setAppName()等操作,然后,调用new SparkContext(conf)来创建sc。紧接着,打印出日志“INFO SparkILoop: Created spark context..”,并将sc返回。

通过上述的一系列操作,对spark shell环境的初始化就初步完成了。如下图所示:

spark源码分析:(一)spark-shell启动脚本时候过程_第1张图片

你可能感兴趣的:(spark源码分析:(一)spark-shell启动脚本时候过程)