Scala内核Spark阻塞排查
背景
在Jupyter Scala内核(Almond Scala内核),当运行Spark SQL时会偶发的卡主(Jupyter lab还显示running状态,但到Spark ui中查看已经运行结束了)
由于排查内核问题,成本较高,所以想先简单调研下其他的Jupyter Scala内核实现
其他内核实现简单调研
目前开源的Jupyter Scala内核:参考链接
Almond (当前我们所选方案)
启动内核后可以根据自己需要随意配置Spark并启动,输出数据比较美观,文档丰富,社区较为活跃
IScala
依赖于IPython,Scala 2.10.2,没有使用Spark相关案例,项目不活跃,最近一次提交已经是七年前
Toree
安装内核的时候就要指定好Spark配置,启动内核就相当于执行spark-submit,用户使用不友好不方便,输出数据展示不好看
安装
总结
Toree和IScala无法灵活配置Spark,输出样式不美观,社区活跃度不高,所以只能继续使用Almond,开始尝试修复Almond当前bug
尝试排查问题
使用 papermill 执行打debug日志
命令
发现内核卡在某个stage,更新task非常慢,偶尔超时直接内核挂掉,有时一直卡住,因为使用的是papermill,所以直接排除了Jupyter lab端导致的问题
后来发现当一个stage中task过多的时候就基本会稳定复现这个问题,比如下面这段代码
代码
直接本地调试(进入到调试状态鬼知道经历了什么!!)打日志发现,是发送的消息超过限制了

由于jupyter client和内核层消息框架用的是zeromq,所以简单了解了一下zeromq的消息机制,由于使用的pub/sub模式,直接找到对应的 介绍文档

所以当zero sub端接收消息超出限制(jupyter server端配置1000msgs/sec),就会丢弃后面的消息,所以很明显问题就是出在了丢弃消息上,也就是执行状态的消息被丢失了,解决这个问题有几种思路
调大jupyter server端的1000msgs/sec配置
降低消息输出速率
1 Jupyter官方设置这个默认值肯定有他的深意,不想随意调整默认值
2 找到产出消息的源头,我们是否可以降低这个消息的频率,避免最终执行状态的消息被丢弃
到Almond代码中找到对应的Spark监听类
ProgressSparkListener.scala
所以Almond-Spark进度条当一个stage中的task开始或者结束都会执行elem.update(),调用publish.html("spark progress info..."),更新当前的进度信息,并发送消息到Jupyter Client,在回头看我们之前执行的代码
这个会产生一个有5000个task的stage,也就是说会发送一万条的消息,当spark执行task比较快的时候,比如说每秒有1000个以上的任务有启停动作,就会导致jupyter丢弃相关的消息,导致最终执行状态的消息也有可能被丢弃,所以Jupyter Lab显示一直running状态
对方案2(降低产出消息速率)进行两种尝试:
直接不发送进度信息,直接注释掉
降低发送进度信息速率,定时更新进度,每秒更新一次
再次实验,两种方案都可以完美解决,Git地址
附:编译环境准备
http://dblab.xmu.edu.cn/blog/maven-network-problem/
图1

依赖冲突,必须exclude一下依赖,再添加合适的依赖版本

exclude依赖

添加合适版本的依赖

最后更新于