sbt结合IDEA对Spark进行断点调试开发

原创文章,谢绝转载html

笔者出于工做及学习的目的,常常与Spark源码打交道,也不免对Spark源码作修改及测试。本人一贯讲究借助工具提高效率,开发Spark过程当中也在摸索如何更加顺畅的对源码进行调试。java

Spark基于Scala,采用IntelliJ IDEA和sbt应对平常开发,天然是最佳选择了。如何导入及编译Spark项目,网上资料不少,官网给的教程也比较详细:git

本文基于Spark2.x的源码,重点介绍如何使用sbt结合IDEA对Spark进行断点调试开发,这对于常常修改或学习Spark源码的读者较为有益。废话到此,咱们进入正题。github

Spark源码编译

首次拿到Spark源码,直接导入IDEA会有不少错误,由于SQL项目的catalyst中的SQL语法解析依赖ANTLR语法定义,须要经过编译生成代码,以下是采用sbt打包编译的流程:sql

git clone https://github.com/apache/spark.git
cd spark
build/sbt package

...通过漫长等待,成功编译后,导入IDEA就能够正常看源码了。apache

你们能够采用阿里云的Maven仓库,加速下包的过程,能够参考个人这篇文章:https://zhuanlan.zhihu.com/p/25279570jvm

编写测试用例

我习惯于直接在Spark项目中写TestCase的方式做为执行Spark的入口,这种方式对于常常修改Spark源码的开发场景很适用,相比在SparkShell中写测试代码有如下好处:socket

  • 代码保留在文件中,方便修改从新执行
  • 代码在同一个项目中,源码修改后IDEA无需对代码进行二次索引
  • 方便进行持续测试(Continuous Test)

Spark源码自带大量的TestCase可供咱们学习参考,咱们以Spark的SQL项目为例,将spark/sql/core/src/test/scala/org/apache/spark/sql/SQLQuerySuite.scala复制为SimpleSuite.scalaide

注意,这里不要是使用IDEA自带的复制功能,由于IDEA在复制的时候会从新组织代码中import的次序,这有可能会致使编译出错。正确的姿式应该是:工具

  1. 在IDEA中,找到要复制的文件,右击,复制代码路径

  1. 在IDEA的Terminal窗口中执行cp xxx xxx2完成复制

咱们之因此要基于SQLQuerySuite复制出一个SimpleSuite文件是由于:Spark为了确保代码风格一致规范(好比每一个代码文件头部须要定义Apache的License注释;import的顺序为java,scala,3rdParty,spark),在项目引入了Scala-style checker,若是代码不合规范,执行编译会出错。直接复制一个文件在上面作修改能够避免踩到代码风格检查的坑。我将SimpleSuite的内容修改以下:

打开IDEA的Terminal窗口,执行build/sbt进入sbt的交互式环境,经过如下方式执行咱们的SimpleSuite:

> project sql
> testOnly *SimpleSuite

project sql指的是切换到SQL项目,这样在执行testOnly时能够快速定位到咱们的SimpleSuite类,能够执行projects查看Spark定义的全部子模块,当前所在的模块名称前会有个*的标识。首次执行测试的时间比较长,再次执行就会比较快了,若是测试经过的话,会看到以下信息:

在sbt中执行exit退出交互式环境,接下来介绍如何使用sbt结合IDEA进行断点调试。

sbt结合IDEA对Spark进行断点调试

因为sbt是在Terminal中单独启动的进程,要对sbt调试,就须要采用IDEA的远程调试功能了。在IDAE的菜单中选择Run -> Edit Configrations...,在接下来的窗口中添加一个Remote配置:

配置名称你们随意,我这里为Spark,远程调试的端口为5005,若是本地的5005端口被占用,改成其余端口便可。

而后回到Terminal从新启动sbt,启动时须要添加远程调试参数:build/sbt -jvm-debug 5005,启动过程当中会提示Listening for transport dt_socket at address: 5005,启动sbt后,咱们就能够经过IDEA对sbt进行调试了。

接下来咱们给SimpleSuite的test方法内部随意添加一个断点,回到sbt执行:

> project sql
> set fork in Test := false
> testOnly *SimpleSuite

一切顺利的话,执行testOnly的过程当中,咱们的断点会被命中:

若是对Spark源码或SimpleSuite的代码作了修改只须要从新执行testOnly *SimpleSuite便可。

让IDEA命中断点有一个关键的语句:set fork in Test := false,这个语句的做用是让sbt执行Test时避免fork子进程。咱们启动sbt的时候添加的远程调试端口是加在sbt上的,若是执行Test不在一个进程内,IDEA就没法命中断点。

若是频繁修改代码,反复执行testOnly不免有些不便,咱们能够采用sbt的持续编译功能简化流程。执行时加上~,也就是~testOnly *SimpleSuite,这样,咱们修改代码,在保存,sbt会监控文件变化并自动执行测试,超级方便。这种方式一样适用于compile,test,run等命令。

总结

几个关键点:

# Spark源码目录下执行(以SimpleSuite为例):
$ build/sbt -jvm-debug 5005
> project sql
> set fork in Test := false
> testOnly *SimpleSuite

OK,掌握以上技巧,咱们就能够愉快的深刻Spark源码内部,了解Spark的运做机制了。

相关文章
相关标签/搜索