Spark源码分析

过程描述:

1.通过Shell脚本启动Master,Master类继承Actor类,通过ActorySystem创建并启动。

2.通过Shell脚本启动Worker,Worker类继承Actor类,通过ActorySystem创建并启动。

3.Worker通过Akka或者Netty发送消息向Master注册并汇报自己的资源信息(内存以及CPU核数等),以后就是定时汇报,保持心跳。

4.Master接受消息后保存(源码中通过持久化引擎持久化)并发送消息表示Worker注册成功,并且定时调度,移除超时的Worker。

5.通过Spark-Submit提交作业或者通过Spark Shell脚本连接集群,都会启动一个Spark进程Driver。

6.Master拿到作业后根据资源筛选Worker并与Worker通信,发送信息,主要包含Driver的地址等。

7.Worker进行收到消息后,启动Executor,Executor与Driver通信。

8.Driver端计算作业资源,transformation在Driver 端完成,划分各个Stage后提交Task给Executor。

9.Exectuor针对于每一个Task读取HDFS文件,然后计算结果,最后将计算的最终结果聚合到Driver端或者写入到持久化组件中。

Spark启动Shell脚本

1.start-all.sh

可以中脚本文件看出,spark启动事首选启动spark-config.sh,然后启动start-master.sh 最后启动脚本

2.start-master.sh脚本

${SPARK_HOME}/sbin"/spark-daemon.sh start $CLASS1 \
--ip $SPARK_MASTER_IP--port $SPARK_MASTER_PORT--webui-port $SPARK_MASTER_WEBUI_PORT\
 $ORIGINAL_ARGS/sbin"/spark-daemon.sh start $CLASS1 \
--ip $SPARK_MASTER_IP--port $SPARK_MASTER_PORT--webui-port $SPARK_MASTER_WEBUI_PORT\
 $ORIGINAL_ARGS

start-master.sh脚本主要执行的是这一块逻辑,主要使用spark-daemon.sh利用传入的参数启动Spark。

3.spark-daemon.sh脚本

nohup nice -n"$SPARK_NICENESS""${SPARK_HOME}"/bin/spark-class $command"$@">> "$log"2>&1 < /dev/null &-n"$SPARK_NICENESS""${SPARK_HOME}"/bin/spark-class $command"$@">> "$log"2>&1 < /dev/null &

脚本中启动执行的主要脚本命令是上述代码,主要通过${SPARK_HOME}/bin/spark-class启动,接下来我们看一下spark-class

4.spark-class文件

done< <("$RUNNER"-cp"$LAUNCH_CLASSPATH"org.apache.spark.launcher.Main "$@")
exec "${CMD[@]}"
​
//这里是$RUNNER代表的含义
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
   exit1
 fi
fi
//这里是$LAUNCH_CLASSPATH表示的含义
SPARK_ASSEMBLY_JAR=
if[ -f"${SPARK_HOME}/RELEASE"]; then
 ASSEMBLY_DIR="${SPARK_HOME}/lib"
else
 ASSEMBLY_DIR="${SPARK_HOME}/assembly/target/scala-$SPARK_SCALA_VERSION"
fi
​
SPARK_ASSEMBLY_JAR="${ASSEMBLY_DIR}/${ASSEMBLY_JARS}"
​
LAUNCH_CLASSPATH="$SPARK_ASSEMBLY_JAR"< <("$RUNNER"-cp"$LAUNCH_CLASSPATH"org.apache.spark.launcher.Main "$@")
exec "${CMD[@]}"
​
//这里是$RUNNER代表的含义
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
   exit1
 fi
fi
//这里是$LAUNCH_CLASSPATH表示的含义
SPARK_ASSEMBLY_JAR=
if[ -f"${SPARK_HOME}/RELEASE"]; then
 ASSEMBLY_DIR="${SPARK_HOME}/lib"
else
 ASSEMBLY_DIR="${SPARK_HOME}/assembly/target/scala-$SPARK_SCALA_VERSION"
fi
​
SPARK_ASSEMBLY_JAR="${ASSEMBLY_DIR}/${ASSEMBLY_JARS}"
​
LAUNCH_CLASSPATH="$SPARK_ASSEMBLY_JAR"

从上面的代码可以看出主要是通过获取Java环境然后启动Spark程序

Spark Submit脚本

关于作业提交的主要脚本是spark-submit脚本,主要执行的代码是

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

这里主要的用到的类是org.apache.spark.deploy.SparkSubmit,接下来我们将要看SparkSubmit代码,这里的Spark代码基于Spark1.3.1

SparkSubmit类

/bin/spark-submit \
 --classcn.edu.hust.WordCount \
 --masterspark://207.184.161.138:7077 \
 --executor-memory20G \
 --total-executor-cores100\
/path/to/examples.jar \
 1000
 --classcn.edu.hust.WordCount \
 --masterspark://207.184.161.138:7077 \
 --executor-memory20G \
 --total-executor-cores100\
/path/to/examples.jar \
 1000

下面是SparkSubmit类的时序图:

具体叙述流程如下 :

 

1.SparkSubmit在main()方法中执行,然后根据提交的类型调用相应的方法,这里是"Submit",调用submit()方法,submit()里面进行一些判断后,使用反射Class.forName(childMainClass, true, loader),然后调用invoke()方法来调用程序员自己写的类,我们这里是WordCount。

2.在WordCount类中,main()方法里有调用SparkContext,SparkContext构造器使用createSparkEnv()方法,这个方法使用SparkEnv.createDriverEnv(conf, isLocal, listenerBus)方法创建SparkEnv对象;在SparkEnv类,调用create()方法来进行创建SparkEnv,在这个方法内部,有一个AkkaUtils.createActorSystem(actorSystemName, hostname, port, conf, securityManager)的调用过程,主要用来产生Akka中的ActorSystem以及得到绑定的端口号。

 

 

3.在创建SparkEnv对象后,SparkContext构造器使用代码SparkContext.createTaskScheduler(this, master)创建TaskScheduler对象,这里根据实际的提交模式来进行创建TaskScheduler对象,提交模式有:local、Mesos、Zookeeper、Simr、Spark,这里模们主要分析Spark集群下的模式;然后还需要创建一个SparkDeploySchedulerBackend对象;在创建TaskScheduler对象调用initialize()方法,这里选择调度模式,主要有两种模式,FIFO和FAIR,默认的调度模式;最后调用taskScheduler的start()方法,里面主要调用SparkDeploySchedulerBackend对象的start()方法,首先调用父类的start()方法产生一个用于和Executor通信的DriverActor对象,然后里面主要创建一个AppClient对象内部有ClientActor类对象,用于Driver和Master进行RPC通信。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值