百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 博客教程 > 正文

Spark源码阅读:从Spark-Shell开始(下)

connygpt 2024-12-26 12:52 11 浏览

在上一篇文章 Spark源码阅读:从spark-shell开始(上) 中,我们发现 spark-shell 是通过 spark-submit 脚本启动的,两个脚本都只是做了一层包装,底层逻辑spark源码里的 Scala单例类 SparkSubmit。object SparkSubmit 负责提交一个spark任务,而 object repl.Main 作为参数。

为了看到真实的提交结果,我们在执行时,设置SPARK_PRINT_LAUNCH_COMMAND=1,执行结果如下:

输出到console的信息可以分为五个部分:

  1. 背后真正的启动命令
  2. 初始化 Logging 的输出
  3. 初始化 repl 命令行的输出:预加载类/配置,初始化SparkSession
  4. 打印Welcome
  5. 交互式命令行开始接收输入

启动命令比较简单,java 执行 object SparkSubmit 的 main函数,SparkSubmit.main 里通过反射获取 object repl.Main 的 main 函数,并触发执行:

/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/bin/java \
  -cp /Users/user/go/src/github.com/apache/spark/conf/:/Users/user/go/src/github.com/apache/spark/assembly/target/scala-2.12/jars/* \
  -Dscala.usejavacp=true \
  -Xmx1g org.apache.spark.deploy.SparkSubmit \
  --class org.apache.spark.repl.Main \
  --name Spark shell spark-shell

我们再看一下 object repl.Main 的结构。它存储了一些配置信息,还提供了创建SparkSession对象的能力。值得注意的是,sparkSession: SparkSession 和 交互式命令行里的 spark: SparkSession 是同一个对象。交互式命令行的初始化和代码执行均托管给 interp: SparkILoop 对象。

SparkILoop 实现了Spark定制版的交互式命令行。SparkILoop 继承了 scala 编译器自带的 repl 类 ILoop,所以已经具备了命令行的核心功能(读取用户输入,解释/执行代码,返回输出),它只需要定制命令行的启动逻辑即可。startup() 函数中对这段逻辑有完整的实现,后面我们着重阅读这部分代码。

上面这张图左侧是 SparkILoop 的结构,process方法包含了创建命令行的主干逻辑,启动阶段的逻辑包含在 startup 方法中。startup() 阶段,会按顺序创建:

  1. SplashLoop实例:启动后台线程,存储用户输入
  2. scala解释器实例:用来解释&执行用户输入的代码
  3. SparkSession实例:用来执行spark指令

然后返回初始化过程中的用户输入。

上图中右侧的代码中,startup() 返回的字符串,通过 pattern matching 进行检查。

Pattern Matching (模式匹配) 用来检查一个变量是否符合某种pattern,是函数式编程的标配。使用时,它可以取代 Java 里的 switch case,但能力远远不止于此。

如果初始化过程中,用户按了 Ctrl+D ,返回值是 null,命令行会退出执行。

如果输入了其他字符,loop(line) 里的 loop方法会解释并执行这行代码,并等待用户下一轮输入。loop 方法是 SparkILoop 父类 ILoop 的方法。它是一个尾递归函数,除非接收到 null,或者遇到不可恢复的异常,否则不会退出。

// class ILoop
@tailrec final def loop(line: String): LineResult = {
  import LineResults._
  if (line == null) EOF
  else if (try processLine(line) catch crashRecovery) loop(readOneLine())
  else ERR
}

private def readOneLine() = {
  out.flush()
  in readLine prompt
}

processLine 方法会处理新的指令。对于有很多行的代码块,解释器对象判断是否缓存代码,或取出缓存代码触发执行。

readOneLine 会尝试读取新的用户输入,如果没有,则处于等待状态。


命令行启动的主流程基本是这些。刚才我们提到,startup() 阶段,会按顺序创建:

  1. SplashLoop实例:初始化 console reader,并提取reader获取的第一条指令
  2. scala解释器实例:用来解释&执行用户输入的代码
  3. SparkSession实例:用来执行spark指令

下面我们看一下 startup() 的逻辑。

// 创建 SplashLoop,包含SplashReader实例和 prompt
val splash = preLoop

// 创建解释器
createInterpreter()
intp.initializeSynchronous()

// 更新配置,初始化SparkSession,执行预加载文件和代码
loopPostInit()

// 打印welcome
printWelcome()

// 提取第一行,Ctrl+D 停止执行
val line = splash.line

这些步骤走完之后,loop 方法会接管第一行输入,并处理完startup过程中用户输入的指令,同时接收新的用户输入指令。

这里我们重点看下如何初始化 SparkSession。初始化的逻辑定义在 initializeSpark 函数中。IMain intp 是一个 scala 的代码解释器,它通过 quietRun 可以执行代码块。

// class SparkILoop
def initializeSpark(): Unit = {
  if (!intp.reporter.hasErrors) {
    // `savingReplayStack` removes the commands from session history.
    savingReplayStack {
      initializationCommands.foreach(intp quietRun _)
    }
  } else {
    throw new RuntimeException(s"Scala $versionString interpreter encountered " +
      "errors during initialization")
  }
}

// 初始化命令,总共5条
val initializationCommands: Seq[String] = Seq(
  """
  @transient val spark = if (org.apache.spark.repl.Main.sparkSession != null) {
      org.apache.spark.repl.Main.sparkSession
    } else {
      org.apache.spark.repl.Main.createSparkSession()
    }
  @transient val sc = {
    val _sc = spark.sparkContext
    // 打印 WebUI URL 和 application id
    ...
    _sc
  }
  """,
  "import org.apache.spark.SparkContext._",
  "import spark.implicits._",
  "import spark.sql",
  "import org.apache.spark.sql.functions._"
)

initializationCommands 包含五个代码块,第一个用来初始化 spark 和 sc对象,其余用于在命令行里引入必要的package和class。这段代码执行完成,意味着我们可以在命令行里使用 spark 和 sc 对象。

值得注意的是 spark 的初始化是通过 object org.apache.spark.repl.Main 的方法实现的,而 repl.Main 是 spark-shell 的入口单例类。命令行里和的 spark 对象 和 repl.Main.sparkSession 成员变量是同一个 SparkSession,同理对 sc 和 repl.Main.sparkContext 也是一个。

spark-shell 代码的介绍就先到这里。如果你对于spark代码不熟悉,可以走单步调试,看关键步骤的执行是否符合预期。配置方式如下:

第一步:IDEA 增加 Remote JVM Debug 配置,这里我们只选择 use module class=spark-repl,其他使用默认值

第二步:启动 spark-shell前设置环境变量 SPARK_SUBMIT_OPTS

export SPARK_SUBMIT_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005
./bin/spark-shell

// spark-shell 命令会被卡在下面这一行,直到执行第三步
Listening for transport dt_socket at address: 5005

第三步:IDE 里启动 Debug

相关推荐

Golang报表项目的Java重构过程

最近,完成了从原先使用Golang生成报表的工程项目,到Java项目来重构。【背景内容】最初使用Golang,主要是由于语言自身的精简性。不管是搭建HTTP服务端,还是对传统数据库的数据获取,都有...

不懂代码也能做出酷炫可视化大屏

最近在做一个数据可视化大屏项目,从指标设计、视觉设计、可视化动效到大屏硬件、开发工具整个流程,总结了一些经验和观点,想和大家分享。大屏制作工具大屏的制作,可以用代码开发或现成的可视化工具来实现。用的比...

爬虫界的启蒙老师,python超爽爬取入门案例分享

爬虫,就是授权的或公开数据的自动采集。百度,就是一只爬虫,一条百足之虫。学会爬虫,会让你以为自己离超越百度指日可待。人有多大胆,地有多大产,梦想还是要有的,万一实现了呢。人不怕有梦想,就怕不知道,不敢...

多亏这几个工具,我终于搞懂了数据分析怎么做

说起来,数据分析这东西之前真是让我头疼不已,感觉就像是个无解的迷宫。但多亏这几个工具,竟然帮我打开了这扇大门,让我终于搞明白了数据分析是怎么一回事。数据分析可不仅仅是个简单的任务,它涉及数据收集、处理...

主流富文本编辑器推荐,网站编辑器排名不分先后及特点

富文本编辑器(RichTextEditor,简称RTE)是一种提供类似于MicrosoftWord编辑功能的工具,在后台处理文章编辑时,深受不会编写HTML但又需要设置各种文本格式的用户喜爱,但...

文库系统开发全攻略:技术要点与实战案例

在信息爆炸的时代,文库系统作为知识管理的基石,正面临着前所未有的挑战。随着人工智能技术的崛起,我们有机会重新定义文档的存储、检索和交互方式。本文将深入探讨AI技术如何赋能文库系统,提升其智能化水平,从...

Apache ECharts:基于JavaScript的数据可视化图表库

#挑战30天在头条写日记#ApacheECharts是一个由百度开源的数据可视化,凭借着良好的交互性,精巧的图表设计,得到了众多开发者的认可。而Python是一门富有表达力的语言,很适合用于数...

某个网站打不开,其它网站都正常怎么办?

前两天学echarts,结果官网打不开,其它网站都能正常打开,自己家里的网络打不开echarts官网,把echarts官网地址发给朋友都能打开,然后综合网上查看的资料,确定是dns的问题,有两种方法...

ECharts 水球图

水球图是一种适合于展现单个百分比数据的图表类型,ECharts的水球图插件使你能够通过非常简单的配置,实现酷炫的数据展示效果。那么,今天我们就一起来学习一下,如何使用ECharts水球图。第一步...

用echarts画图表好,还是dhtmlxGantt画图表好?实战验结果

事情是这样的早些时候,我接到了一个需求,说要将项目里程碑用甘特图展示,一脸懵逼的我先是搜一下什么是“甘特图”:From百度百科:甘特图(Ganttchart)又称为横道图、条状图(Barch...

轻量级 Markdown 写作工具:One Markdown

大家好,我是oulvhai,MWeb的作者,MWeb是macOS和iOS/iPadOS下的Markdown写作/记笔记/静态博客生成软件。所以简单地来说我就是专门做ma...

【大屏可视化组态编辑器】图表

大屏中的图表大使用的是开源可视化图表库Echarts在线编辑:https://v.le5le.com/使用1.在html中引入echarts资源包<scriptsrc="ech...

20多个好用的 Vue 组件库,请查收!

每日一荐:Freemen,程序员自己的求职招聘软件,赶紧下载收藏一波,留着下次跳槽用。在本文中,我们将探讨一些最常见的vuejs组件。你可以收藏一波。VueTables-2地址:https://g...

弃用 Echarts,推荐选择Vue Data UI!

各位网友大家好,今天,我要向大家隆重推荐一款令人惊艳的可视化图表库——VueDataUI,一个赋予用户权力的数据可视化Vue3组件库。前言VueDataUI诞生于一个问题:如果你的仪表板...

Echarts仿电梯运行图

本文适合有一定Echarts基础的人员,至少可以面向API编程。场景假设我们有这样一个需求:实现一个柱状图,柱状图中间有一个小块表示电梯,柱状图本身作为建筑物。而且电梯需要上下运行动画。实现工具优先选...