Spark有几种部署的模式,单机版、集群版等等,平时单机版在数据量不大的时候可以跟传统的java程序一样进行断电调试、但是在集群上调试就比较麻烦了…远程断点不太方便,只能通过Log的形式进行数据分析,利用spark ui做性能调整和优化。
大体上会按照下面的思路进行讲解:

  • 怎么访问Spark UI
  • SparkUI能看到什么东西?job,stage,storage,environment,excutors
  • 调优的一些经验总结

一、Spark UI入口

如果是单机版本,在单机调试的时候输出信息中已经提示了UI的入口:

  1. 17/02/26 13:55:48 INFO SparkEnv: Registering OutputCommitCoordinator
  2. 17/02/26 13:55:49 INFO Utils: Successfully started service 'SparkUI' on port 4040.
  3. 17/02/26 13:55:49 INFO SparkUI: Started SparkUI at http://192.168.1.104:4040
  4. 17/02/26 13:55:49 INFO Executor: Starting executor ID driver on host localhost

单机调试的时候,可以直接登陆:http://192.168.1.104:4040
如果是集群模式,可以通过Spark日志服务器xxxxx:18088者yarn的UI进入到应用xxxx:8088,进入相应的Spark UI界面。

image.png
上面就是Spark的UI主页,首先进来能看到的是Spark当前应用的job页面,在上面的导航栏:

  • 1 代表job页面,在里面可以看到当前应用分析出来的所有任务,以及所有的excutors中action的执行时间。
  • 2 代表stage页面,在里面可以看到应用的所有stage,stage是按照宽依赖来区分的,因此粒度上要比job更细一些
  • 3 代表storage页面,我们所做的cache persist等操作,都会在这里看到,可以看出来应用目前使用了多少缓存
  • 4 代表environment页面,里面展示了当前spark所依赖的环境,比如jdk,lib等等
  • 5 代表executors页面,这里可以看到执行者申请使用的内存以及shuffle中input和output等数据
  • 6 这是应用的名字,代码中如果使用setAppName,就会显示在这里
  • 7 是job的主页面。

二、模块讲解

程序代码如下:

  1. public static void main(String[] args) throws InterruptedException {
  2. SparkConf sparkConf = new SparkConf();
  3. sparkConf.setMaster("local[2]");
  4. sparkConf.setAppName("test-for-spark-ui");
  5. JavaSparkContext sc = new JavaSparkContext(sparkConf);
  6. //知识,哪怕是知识的幻影,也会成为你的铠甲,保护你不被愚昧反噬。
  7. JavaPairRDD<String,Integer> counts = sc.textFile( "C:\\Users\\xinghailong\\Desktop\\你为什么要读书.txt" )
  8. .flatMap(line -> Arrays.asList(line.split(" ")).iterator())
  9. .mapToPair(s -> new Tuple2<String,Integer>(s,1))
  10. .reduceByKey((x,y) -> x+y);
  11. counts.cache();
  12. List<Tuple2<String,Integer>> result = counts.collect();
  13. for(Tuple2<String,Integer> t2 : result){
  14. System.out.println(t2._1+" : "+t2._2);
  15. }
  16. sc.stop();
  17. }

这个程序首先创建了SparkContext,然后读取文件,先使用进行切分,再把每个单词转换成二元组,再根据key进行累加,最后输出打印。为了测试storage的使用,这里对计算的结果添加了缓存。

三、job页面

image.png
主页可以分为两部分,一部分是event timeline,另一部分是进行中和完成的job任务。
image.png

第一部分event timeline展开后,可以看到executor创建的时间点,以及某个action触发的算子任务,执行的时间。通过这个时间图,可以快速的发现应用的执行瓶颈,触发了多少个action。
第二部分的图表,显示了触发action的job名字,它通常是某个count,collect等操作。有spark基础的人都应该知道,在spark中rdd的计算分为两类,一类是transform转换操作,一类是action操作,只有action操作才会触发真正的rdd计算。具体的有哪些action可以触发计算,可以参考api。collect at test2.java:27描述了action的名字和所在的行号,这里的行号是精准匹配到代码的,所以通过它可以直接定位到任务所属的代码,这在调试分析的时候是非常有帮助的。Duration显示了该action的耗时,通过它也可以对代码进行专门的优化。最后的进度条,显示了该任务失败和成功的次数,如果有失败的就需要引起注意,因为这种情况在生产环境可能会更普遍更严重。点击能进入该action具体的分析页面,可以看到DAG图等详细信息。

四、stage页面

image.png
窄依赖是指前一个rdd计算能出一个唯一的rdd,比如map或者filter等;宽依赖则是指多个rdd生成一个或者多个rdd的操作,比如groupbykey reducebykey等,这种宽依赖通常会进行shuffle。
因此Spark会根据宽窄依赖区分stage,某个stage作为专门的计算,计算完成后,会等待其他的executor,然后再统一进行计算。
image.png
stage页面的使用基本上跟job类似,不过多了一个DAG图。这个DAG图也叫作血统图,标记了每个rdd从创建到应用的一个流程图,也是我们进行分析和调优很重要的内容。比如上面的wordcount程序,就会触发acton,然后生成一段DAG图:
image.png
从这个图可以看出,wordcount会生成两个dag,一个是从读数据到切分到生成二元组,第二个进行了reducebykey,产生shuffle。
点击进去还可以看到详细的DAG图,鼠标移到上面,可以看到一些简要的信息。
image.png

五、storage页面

storage页面能看出目前使用的缓存,点击进去可以看到具体在每个机器上,使用的block的情况。
image.png

六、environment页面

这个页面一般不太用,因为环境基本上不会有太多差异的,不用时刻关注它。
image.png

七、excutors页面

image.png
这个页面比较常用了,一方面通过它可以看出来每个excutor是否发生了数据倾斜,另一方面可以具体分析目前的应用是否产生了大量的shuffle,是否可以通过数据的本地性或者减小数据的传输来减少shuffle的数据量。

八、Spark UI参数

当Spark程序在运行时,会提供一个Web页面查看Application运行状态信息。是否开启UI界面由参数spark.ui.enabled(默认为true)来确定。下面列出Spark UI一些相关配置参数,默认值,以及其作用。

参数 默认值 作用描述
spark.ui.enabled true 是否开启UI界面
spark.ui.port 4040(顺序探查空闲端口) UI界面的访问端口号
spark.ui.retainedJobs 1000 UI界面显示的Job个数
spark.ui.retailedStages 1000 UI界面上显示的Stage个数
spark.ui.timeline.tasks.maximum 1000 Stage页面显示的Tasks个数
spark.ui.killEnabled true 是否运行页面上kill任务
spark.ui.threadDumpsEnabled true Executors页面是否可以展示线程运行状况

本文接下来分成两个部分,第一部分基于Spark-1.6.0的源码,结合第二部分的图片内容来描述UI界面在Spark中的实现方式。第二部分以实例展示Spark UI界面显示的内容。