Spark源码初探-从spark-submit开始

深入了解spark的运行机制是为了搭建集群,编写应用能达到更好的性能表现,甚至更加深入的了解还可以自己修改源码,实现定制功能来适配自己的需求。仅仅通过一些spark架构和运行原理的描述来了解spark是远远不够的,所以打算系统的看一下spark的源码,以添加注释的方式写下自己的理解(尽量写的详细,很可能会显得啰嗦)。spark源码的版本为2.0.1,是当前最新的版本,平时抽点时间慢慢看,能看多少算多少了。----序

下面从我们提交spark应用使用的spark-submit脚本开始spark的源码阅读之旅。如果有看官发现有错误的地方,还望不吝赐教。

file://bin/spark-submit

#检查环境变量中是否有SPARK_HOME,如果不存在则设置环境变量
#dirname命令:输出已经去除了尾部的"/"字符部分的名称;如果名称中不包含"/",显示"."
#反引号在Linux中起着命令替换的作用。命令替换是指shell能够将一个命令的标准输出插在一个命令行中任何位置。
#$0表示当前bash文件绝对路径名,下面一段的作用就是:如果SPARK_HOME不存在,则设置这个环境变量
if [ -z "${SPARK_HOME}" ]; then
  export SPARK_HOME="$(cd "`dirname "$0"`"/..; pwd)"
fi

# disable randomized hash for string in Python 3.3+
export PYTHONHASHSEED=0
#执行spark-class脚本,添加参数org.apache.spark.deploy.SparkSubmit,$@表示将spark-submit后面的参数也传给spark-class
exec "${SPARK_HOME}"/bin/spark-class org.apache.spark.deploy.SparkSubmit "$@"

#可以看到spark-submit只检查了环境变量,并没有做更多的事情,接下来看看spark-class脚本是如何运作的

file://bin/spark-class

#再次检查SPARK_HOME环境变量,如果不存在就export一个。
if [ -z "${SPARK_HOME}" ]; then
  export SPARK_HOME="$(cd "`dirname "$0"`"/..; pwd)"
fi

#调用了load-spark-env.sh,该脚本的作用是将conf/spark-env.sh中的变量加入环境变量。以及设置SPARK_SCALA_VERSION(scala版本号)。
#需要注意的是该脚本中会判断名为SPARK_ENV_LOADED的环境变量是否存在,如果不存在则export SPARK_ENV_LOADED=1并加载spark-env.sh中的环境变量;
. "${SPARK_HOME}"/bin/load-spark-env.sh

# 找到java命令,为运行jar做准备,如果没有设置JAVA_HOME会报错误,并退出。
if [ -n "${JAVA_HOME}" ]; then
  RUNNER="${JAVA_HOME}/bin/java"
else
  if [ `command -v java` ]; then
    RUNNER="java"
  else
  #如果java命令找不到或者不存在就会输出错误信息 >&2 表示重定向到错误输出
    echo "JAVA_HOME is not set" >&2
    exit 1
  fi
fi

# 找到spark的依赖jar
#RELEASE文件可以在官网下载的预编译包的根目录下看到,里面记载了编译说明和编译参数,源码编译后会生成这个文件
#值得注意的是在2.0版本以后依赖jar都放到了${SPARK_HOME}/jars目录下,而在1.X版本放在${SPARK_HOME}/lib目录下
if [ -f "${SPARK_HOME}/RELEASE" ]; then
  SPARK_JARS_DIR="${SPARK_HOME}/jars"
else
#如果是用户自己用mvn或sbt编译的话,依赖也可能在{SPARK_HOME}/assembly/target/scala-$SPARK_SCALA_VERSION/jars目录下
  SPARK_JARS_DIR="${SPARK_HOME}/assembly/target/scala-$SPARK_SCALA_VERSION/jars"
fi

#如果SPARK_JARS_DIR不是目录或者$SPARK_TESTING$SPARK_SQL_TESTING这两个变量(这两个变量猜测可能是编译源码时设定的spark测试标志)不存在,就报错,退出。
#$SPARK_TESTING和$SPARK_SQL_TESTING不一定要存在,只需要找到SPARK_JARS_DIR就可以了,在spark2.X版本中依赖全部放在./jars目录下。而1.X版本中是放在lib目录下。
if [ ! -d "$SPARK_JARS_DIR" ] && [ -z "$SPARK_TESTING$SPARK_SQL_TESTING" ]; then
  echo "Failed to find Spark jars directory ($SPARK_JARS_DIR)." 1>&2
  echo "You need to build Spark with the target \"package\" before running this program." 1>&2
  exit 1
else
  LAUNCH_CLASSPATH="$SPARK_JARS_DIR/*"
fi

# Add the launcher build dir to the classpath if requested.如果需要,将启动程序构建目录添加到类路径。
if [ -n "$SPARK_PREPEND_CLASSES" ]; then
  LAUNCH_CLASSPATH="${SPARK_HOME}/launcher/target/scala-$SPARK_SCALA_VERSION/classes:$LAUNCH_CLASSPATH"
fi

# For tests 测试
if [[ -n "$SPARK_TESTING" ]]; then
  unset YARN_CONF_DIR
  unset HADOOP_CONF_DIR
fi

上面的脚本内容就是Spark应用执行前的环境检查等的准备工作。下面是核心部分,注意接下来会两次调用Spark中的代码。

# The launcher library will print arguments separated by a NULL character, to allow arguments with
# characters that would be otherwise interpreted by the shell. Read that in a while loop, populating
# an array that will be used to exec the final command.
#
# The exit code of the launcher is appended to the output, so the parent shell removes it from the
# command array and checks the value to see if the launcher succeeded.
# 这个方法就是进入jvm的入口,下面会调用
build_command() {
  #用-cp 将SPARK_HOME/jars目录下的依赖全部添加进来。将运行org.apache.spark.launcher包下的Main类,并将原来spark-submit后面的参数全部传过来。
  "$RUNNER" -Xmx128m -cp "$LAUNCH_CLASSPATH" org.apache.spark.launcher.Main "$@"
  printf "%d\0" $?
}
分析:在源码中,这个org.apache.spark.luncher.Main类是Java编写的,主要负责对运行参数进行预编译、检查,即在正式执行spark应用之前检查参数是否有误。

CMD=()#定义存放执行结果$?的数组
while IFS= read -d '' -r ARG; do
  CMD+=("$ARG")
done < <(build_command "$@")

COUNT=${#CMD[@]}
LAST=$((COUNT - 1))
LAUNCHER_EXIT_CODE=${CMD[$LAST]}

# Certain JVM failures result in errors being printed to stdout (instead of stderr), which causes
# the code that parses the output of the launcher to get confused. In those cases, check if the
# exit code is an integer, and if it's not, handle it as a special error case.
if ! [[ $LAUNCHER_EXIT_CODE =~ ^[0-9]+$ ]]; then
  echo "${CMD[@]}" | head -n-1 1>&2
  exit 1
fi

if [ $LAUNCHER_EXIT_CODE != 0 ]; then
  exit $LAUNCHER_EXIT_CODE
fi

CMD=("${CMD[@]:0:$LAST}")
exec "${CMD[@]}"

最后必须确定org.apache.spark.luncher.Main对参数的检查结果是没有问题,即exit code为0,才会指定存放到CMD中的命令。
最重要的是发现程序加载的入口类:org.apache.spark.deploy.SparkSubmit ,这个类就是Spark加载应用并运行的起点,下面将沿着这个类进入Spark源码世界。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值