一次spark作业执行后进程无法关闭的原因及解决方案

最近运维的同学频频反映,spark集群作业模式,每次执行完成spark的进程端口都已经关闭了,但是通过命令执行spark作业的进程和端口却无法自动关闭,严重影响其他业务组的作业运行,但是无法关闭的情况不是经常出现,出现频率也不规范,但是执行任务正常,数据清洗加工正常,存储正常,查看日志发现是在作业执行完成会执行sparksession.stop方法,是这个方法堵塞了进程的正常关闭,但是原因从日志上无法分析,考虑从jvm层面进行分析查看,是否是因为内存或者cpu的原因导致,用-jstack -pid 命令进行打印jvm的堆栈:

只复制部分堆栈:

一次spark作业执行后进程无法关闭的原因及解决方案_第1张图片

从堆栈上大致可以看出是main函数的sparksession.stop的线程堵塞了,线程的几种状态在这就不细说了,大家可以自己在google和百度自行查找,但是是什么原因的,仔细一看发现是在等待某个锁的释放,但是为什么会被锁住呢,只能在stop的方法进行查看源码分析了:

一次spark作业执行后进程无法关闭的原因及解决方案_第2张图片

看到synchronized关键字大概能明白了,应该是锁住了,所以寻找锁住的原因,在堆栈中进行find发现了这样的一个线程信息

一次spark作业执行后进程无法关闭的原因及解决方案_第3张图片

一看原来是这个线程获取到了锁,但是为什么进行waiting呢,这个是将要解决的问题了,仔仔细细的看了下,发现应该是跟spark的ContextCleaner有关,这个方法是用来清理内存空间的残留数据,但是它本是个守护线程,并且在作业运行过程中就已经进行清理了,难道是因为一直在清理内存,但仔细想来也不会,因为如果一直在清理空间的话,守护线程怎么会waiting呢,所以又看了一遍发现又找到一个可以看懂的内容:

at org.apache.spark.rpc.RpcTimeout.awaitResult(RpcTimeout.scala:81)

Rpc协议超时?原来是spark进行心跳检测通信的时候超时了,但是询问运维的同学,在堵塞的期间,并没有网络异常报警或者内存空间占满报警,所以应该不是这2个原因,然后google了一下,发现大佬们基本给出2个答案:

1)spark节点宕机了(当然不可能)

2)引发这个问题是由于数据发生了倾斜,导致STW,缩短GC时间可以解决

所以在spark的启动命令加入动态conf参数:

--conf "spark.driver.extraJavaOptions=-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseConcMarkSweepGC

--conf "spark.executor.extraJavaOptions=-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseConcMarkSweepGC

使用了CMS的回收站(jvm参数配置的简单,也可以配置新生代老年代的内存空间,以及回收站),同时修改了spark.network.timeout 从36s改为默认120s,目前运行几天并没有出现以往情况,心里有点窃喜,哈哈,但是不确定是否是因为此原因,所以有明确处理经验的朋友可以留言给我,感谢!。

你可能感兴趣的:(大数据)