为何Spark-Submit作业在EMR集群主节点遗留运行进程?
这个问题我之前帮朋友排查过类似的,核心是驱动进程里有残留的资源或线程阻止JVM正常退出,结合你的场景,咱们一步步拆解解决:
问题根源分析
你的场景里,即使调用了spark.stop(),主节点的Livy进程仍残留,主要有几个核心原因:
- PDFBox资源泄漏:PDFBox处理文档时会打开文件流、字体对象等资源,如果没有手动关闭,这些资源会占用内存并阻止JVM退出——哪怕Spark上下文已经停止。
- 非守护线程残留:驱动程序中可能存在非守护线程(比如PDFBox的后台处理线程、自定义业务线程),JVM会等待所有非守护线程结束才会终止,而
spark.stop()只负责关闭Spark相关线程,管不了这些外部线程。 - Livy会话配置不合理:如果Livy会话没有设置自动超时关闭,即使作业完成,会话进程也会一直挂着占用内存。
具体解决方案
1. 彻底清理PDFBox资源
PDFBox的资源必须手动释放,推荐用Java的try-with-resources语法(自动关闭实现了AutoCloseable接口的对象),示例代码:
import org.apache.pdfbox.pdmodel.PDDocument; // 生成PDF的核心逻辑 try (PDDocument pdfDoc = new PDDocument()) { // 添加页面、写入内容等操作 // ... pdfDoc.save("temp.pdf"); // 保存到本地临时路径或HDFS } catch (Exception e) { // 异常捕获与处理 }
如果你的代码里用到了PDPage、PDFont等其他PDFBox组件,也要确保在使用后调用close()方法,不要依赖垃圾回收来清理资源。
2. 强制终止驱动进程(谨慎使用)
在spark.stop()之后,直接调用System.exit(0)强制JVM退出,这样能确保所有线程被立即终止。但要注意:必须确保所有业务逻辑(比如PDF上传到S3)已经完全完成,否则会中断未完成的操作。示例:
// 先完成所有业务操作:生成PDF、上传至S3 uploadToS3("temp.pdf"); // 关闭Spark上下文 spark.stop(); // 强制退出JVM,彻底终止进程 System.exit(0);
3. 调整Livy会话配置
你可以在提交作业时通过--conf参数指定,或者全局修改Livy的livy.conf文件:
- 设置会话超时时间:
--conf livy.session.timeout=600s(10分钟,可根据你的作业实际时长调整) - 开启超时自动关闭会话:
--conf livy.spark.stopSessionOnTimeout=true
这样作业完成后,会话超时就会自动销毁对应的Livy进程,避免内存长期占用。
4. 优化作业流程(从根源减少驱动压力)
当前你把所有Hive数据收集到驱动节点生成PDF,这本身就会导致驱动内存压力大,还容易引发泄漏。建议改成分布式处理:
- 让每个Executor处理部分数据,生成小PDF片段
- 最后在驱动(或另一个分布式任务)中合并这些片段,再上传S3
这样不仅能减少驱动的内存占用,还能避免大量数据集中到单个节点引发的性能问题。
验证步骤
- 先测试PDF资源清理:只修改代码释放PDFBox资源,提交作业后观察主节点的Livy进程是否自动退出。
- 如果第一步无效,加上
System.exit(0)再测试,确保进程彻底终止。 - 同时调整Livy的超时配置,做兜底保障——即使有异常残留,会话也会自动关闭。
内容的提问来源于stack exchange,提问作者Kieran
相关产品推荐
相关产品推荐

