Apache Spark源码走读之2:Job的提交与运行

数据库 Spark
本文以wordCount为例,详细说明spark创建和运行job的过程,重点是在进程及线程的创建。

实验环境搭建

在进行后续操作前,确保下列条件已满足。

  1. 下载spark binary 0.9.1

  2. 安装scala

  3. 安装sbt

  4. 安装java

启动spark-shell

单机模式运行,即local模式

local模式运行非常简单,只要运行以下命令即可,假设当前目录是$SPARK_HOME

  1. MASTER=local bin/spark-shell 

"MASTER=local"就是表明当前运行在单机模式

local cluster方式运行

local cluster模式是一种伪cluster模式,在单机环境下模拟standalone的集群,启动顺序分别如下

  1. 启动master

  2. 启动worker

  3. 启动spark-shell

master

  1. $SPARK_HOME/sbin/start-master.sh 

 注意运行时的输出,日志默认保存在$SPARK_HOME/logs目录。

master主要是运行类 org.apache.spark.deploy.master.Master,在8080端口启动监听,日志如下图所示

修改配置

  1. 进入$SPARK_HOME/conf目录

  2. 将spark-env.sh.template重命名为spark-env.sh

  3. 修改spark-env.sh,添加如下内容

 

  1. export SPARK_MASTER_IP=localhost 
  2. export SPARK_LOCAL_IP=localhost 

运行worker

  1. bin/spark-class org.apache.spark.deploy.worker.Worker spark://localhost:7077 -i 127.0.0.1  -c 1 -m 512M 


worker启动完成,连接到master。打开maser的web ui可以看到连接上来的worker. Master WEb UI的监听地址是http://localhost:8080

启动spark-shell

MASTER=spark://localhost:7077 bin/spark-shell

如果一切顺利,将看到下面的提示信息。

Created spark context..
Spark context available as sc.

可以用浏览器打开localhost:4040来查看如下内容

  1. stages

  2. storage

  3. environment

  4. executors

wordcount

上述环境准备妥当之后,我们在sparkshell中运行一下最简单的例子,在spark-shell中输入如下代码

scala>sc.textFile("README.md").filter(_.contains("Spark")).count

上述代码统计在README.md中含有Spark的行数有多少

部署过程详解

Spark布置环境中组件构成如下图所示。

Spark cluster components

  • Driver Program 简要来说在spark-shell中输入的wordcount语句对应于上图的Driver Program.

  • Cluster Manager 就是对应于上面提到的master,主要起到deploy management的作用

  • Worker Node 与Master相比,这是slave node。上面运行各个executor,executor可以对应于线程。executor处理两种基本的业务逻辑,一种就是driver programme,另一种就是job在提交之后拆分成各个stage,每个stage可以运行一到多个task

Notes: 在集群(cluster)方式下, Cluster Manager运行在一个jvm进程之中,而worker运行在另一个jvm进程中。在local cluster中,这些jvm进程都在同一台机器中,如果是真正的standalone或Mesos及Yarn集群,worker与master或分布于不同的主机之上。

JOB的生成和运行

job生成的简单流程如下

  1. 首先应用程序创建SparkContext的实例,如实例为sc

  2. 利用SparkContext的实例来创建生成RDD

  3. 经过一连串的transformation操作,原始的RDD转换成为其它类型的RDD

  4. 当action作用于转换之后RDD时,会调用SparkContext的runJob方法

  5. sc.runJob的调用是后面一连串反应的起点,关键性的跃变就发生在此处

 调用路径大致如下

  1. sc.runJob->dagScheduler.runJob->submitJob

  2. DAGScheduler::submitJob会创建JobSummitted的event发送给内嵌类eventProcessActor

  3. eventProcessActor在接收到JobSubmmitted之后调用processEvent处理函数

  4. job到stage的转换,生成finalStage并提交运行,关键是调用submitStage

  5. 在submitStage中会计算stage之间的依赖关系,依赖关系分为宽依赖窄依赖两种

  6. 如果计算中发现当前的stage没有任何依赖或者所有的依赖都已经准备完毕,则提交task

  7. 提交task是调用函数submitMissingTasks来完成

  8. task真正运行在哪个worker上面是由TaskScheduler来管理,也就是上面的submitMissingTasks会调用TaskScheduler::submitTasks

  9. TaskSchedulerImpl中会根据Spark的当前运行模式来创建相应的backend,如果是在单机运行则创建LocalBackend

  10. LocalBackend收到TaskSchedulerImpl传递进来的ReceiveOffers事件

  11. receiveOffers->executor.launchTask->TaskRunner.run

代码片段executor.lauchTask


def launchTask(context: ExecutorBackend, taskId: Long, serializedTask: ByteBuffer) {
    val tr = new TaskRunner(context, taskId, serializedTask)
    runningTasks.put(taskId, tr)
    threadPool.execute(tr)
  }

说了这么一大通,也就是讲最终的逻辑处理切切实实是发生在TaskRunner这么一个executor之内。

运算结果是包装成为MapStatus然后通过一系列的内部消息传递,反馈到DAGScheduler,这一个消息传递路径不是过于复杂,有兴趣可以自行勾勒。

 

责任编辑:彭凡 来源: 博客园
相关推荐

2014-07-23 10:02:11

Spark源码

2014-07-03 15:40:09

Apache Spar

2014-07-15 10:59:58

Spark代码跟读

2011-12-02 13:04:06

Java

2021-08-09 09:00:00

Kubernetes云计算架构

2014-02-14 15:43:16

ApacheSpark

2016-12-20 09:47:38

Apache SparLambda架构

2024-07-22 08:57:58

2016-11-15 14:07:28

Apache SparLambdaHadoop

2014-03-26 10:52:24

Apache Spar

2017-10-10 17:00:11

SparkHadoop数据处理

2018-02-02 15:50:07

决策树Apache Spar数据

2019-04-08 17:11:46

大数据框架Spark

2017-04-01 14:01:50

Apache Spar内存管理

2022-06-01 13:52:11

开源大数据

2011-07-08 15:41:43

oracle job

2021-08-30 18:09:57

鸿蒙HarmonyOS应用

2017-06-26 15:00:17

2017-03-10 16:32:44

Apache Spar大数据工具

2011-03-11 14:02:53

Apache配置
点赞
收藏

51CTO技术栈公众号