文章目录
- 现象描述
- 开始分析
- 1.初步分析dump文件
- 2.AI分析引用关系
- 分析方向2.1 flink BlobServer bug
- 分析方向2.2 和运行环境有关
- 分析方向2.3 和任务有关
- 回到问题本身,思考一下
- 1. seatunnel到底有没有问题
- 2.再次分析zipfile对象
- 3.分析seatunnel es connector 源码
- 4 怀疑EsRestClient(seatunnel里的对象) 对象没有被close
- 5 怀疑es 自带的RestClient 有问题
- 6 继续观察RestClient
- 7 观察有没有CloseableHttpAsyncClient 和这相关的file bug
- 总结
现象描述
使用seatunnel 同步es数据到clickhouse,多次提交批任务后,flink jobmanager 报heap oom
jobmanager 是session 模式,jobmanager 会常驻,不是第一次使用seatunnel同步数据
开始分析
1.初步分析dump文件
通过MAT工具把dump 文件打开,发现,提示和zipfile文件相关,zipfile 确实有很多对象,没有被回收,最终导致heap 内存使用完,报oom
2.AI分析引用关系
把上面的信息交给deepseek后得到结论
问题现象: 383 个 ZipFile$Source 实例占用 1.1GB 内存(72.52%)。
这些实例被 HashMap$Node[] 引用,根源为 Flink 的 BlobServer。
根本原因: BlobServer 的 BLOB 缓存泄漏 Flink 的 BlobServer 负责管理用户上传的二进制对象(如 JAR
文件、配置文件)。如果 BLOB 未被及时清理(如临时文件未删除或缓存未释放),会导致 ZipFile$Source 实例(关联已打开的
ZIP 文件句柄)堆积,占用大量内存。资源未正确关闭 在读取 ZIP 文件时未正确关闭 ZipFile 或 InputStream,导致文件句柄和内存无法释放
分析方向2.1 flink BlobServer bug
由deepseek得出来的结论指向flink 的BlobServer
首先我找了BlobServer 所有的提交历史记录,没有找到和这个有关的提交,一度我怀疑是这个变量里的内容没有被定时任务清除导致,因为这个变量是在thread里的,通过arthas 也不好实时分析,于是我下载了源码,加上日志,重新编译,生成jar 包,重新运行观察。发现这里运行是正常的。
我把同样的dump内容交给qwen 和chatgpt 都指向这里,于是我只有向社区提交了jira ,看看有没有人遇到过这个问题,搞完上面这些,3天时间已经过去,没找到明确的方向
private final ConcurrentHashMap<Tuple2<JobID, TransientBlobKey>, Long> blobExpiryTimes =new ConcurrentHashMap<>();
分析方向2.2 和运行环境有关
于是我把相同的任务换到一个没有这个问题的环境里,多次测试任务后,问题复现,看来和环境没有关系
分析方向2.3 和任务有关
任务读es还要写clickhouse ,于是我把任务sink修改成console,多次测试后,问题复现,看来和任务本身没有关系
回到问题本身,思考一下
为啥之前使用seatunnel没有问题
为啥问题发生在jobmanager
为啥会有这么多zipfile 对象
1. seatunnel到底有没有问题
鉴于之前使用seatunnel是没有问题的,这里我先认为seatunnel 框架本身应该是没有问题的
2.再次分析zipfile对象
多次分析分析无果后,突然灵机一动,既然有这么多zipfile对象,那为啥不分析下这些对象具体是指的哪些对象,路径里的/tmp/jm_xx/xx里的东西引起了我的注意,这些文件都是运行任务时的临时文件交由blobserver管理的,之前也去这些目录看了,也没有这些文件,但是这里依然显示还有线程持有这个文件的句柄。具体来看(deleted) 这个显得不正常,于是我对比其它环境发现不会有这种文件,多次提任务后,此种类型的文件一直增加,经过对比我发现只要是读es任务就会有此问题。
3.分析seatunnel es connector 源码
经过之前的一系列信息
1.读es会有问题
2.问题发生成在jobmanager
幸好有对flink source 知识的积累,让我快速的把目标放在了ElasticsearchSourceSplitEnumerator类里
public class ElasticsearchSourceSplitEnumeratorimplements SourceSplitEnumerator<ElasticsearchSourceSplit, ElasticsearchSourceState> {
EsRestClient esRestClient}
4 怀疑EsRestClient(seatunnel里的对象) 对象没有被close
EsRestClient(seatunnel里的对象,对es restClient 的一些封装)
加上日志后,本地编译seatunnel对象后,运行发现close 已经生效,好像又没有思路了,代码也不多
都是正常运行的。
5 怀疑es 自带的RestClient 有问题
第一步EsRestClient 直接注释掉,多次运行没有问题,相当于没有读数据,就空运行
看来问题肯定在es自带的RestClient 上
观察这个对象在这里的作用,就是一个请求去得到索引信息
String endpoint =String.format("%s/_cat/indices/%s?h=index,docsCount&format=json", hosts.get(0), index);
于是我把代码重写,直接通过CloseableHttpClient 去请求这部分,现象消失,问题解决,
问题进一步定位到RestClient上
String endpoint =String.format("%s/_cat/indices/%s?h=index,docsCount&format=json", hosts.get(0), index);HttpClientUtil httpClientUtil = new HttpClientUtil();CloseableHttpClient httpClient = httpClientUtil.getHttpClient(connConfig);HttpGet httpGet = new HttpGet(endpoint);
6 继续观察RestClient
CloseableHttpAsyncClient 引起了我的注意,我是用的CloseableHttpClient 没问题
public class RestClient implements Closeable {private static final Log logger = LogFactory.getLog(RestClient.class);private final CloseableHttpAsyncClient client;
}
于是我用CloseableHttpAsyncClient 单独验证,问题复现,看来问题在这里
7 观察有没有CloseableHttpAsyncClient 和这相关的file bug
根据关键词一找,还真找到有这个问题,观察下版本修复情况在5.0.1 版本,再看下es 里引用的版本还是4.x, 到这问题找到。
file leak
总结
1.从最后的结论来看,是最底层组件所引发的问题,最后一步步暴露成flink 的heap oom
2.从问题分析的思路来看,还是要抓住问题的核心点,就是变化的是哪部分,包括环境,版本信息,任务信息。
3.要对所用的组件要有深入的了解,这里对flink source 有比较深入的了解,不然我没法直接定位到是ElasticsearchSourceSplitEnumerator 这个类的问题。
4.对MAT使用的分析还是要加深入了解。