FlinkSQL:source端字段太多导致——64KB 问题
Flink 版本1.13,使用
https://github.com/zhp8341/flink-streaming-platform-web
平台上传 Job。
背景
需要同步 kafka 数据到 hive 表,但是需要从 kafka 端提取 1500+ 个字段。
提交任务后,显示失败。
问题追溯
FlinkSQL 提交 Job 后失败,观察 Flink Dashboard 页未产生相应 Job,说明并未上传到 Flink 集群执行。
由此考虑至少没到 TaskManager 处理的阶段,所以排除数据量过大、并行度过低等原因,因为实际上还没开始 source 阶段。
直接看客户端日志,发现关键问题在于:
compiling "StreamExecCalc$3589": Code of method "processElement(Lorg/apache/flink/streaming/runtime/streamrecord/StreamRecord;)V" of class "StreamExecCalc$3589" grows beyond 64 KB
回头来看,这段信息不能说模糊,但确实不确切,在源码里绕了很多路。
最后找遍提到的类和方法,都不及一个 64KB 的信息离问题点近。
于是去 1.13 的源码中找 64KB ,通过 IDEA 的全局查找项目内的 64KB 信息,找到3个:
其中,TableConfigOptions 和 TableConfig 是我们想看的,因为和 Flinksql 有关。ExecutionConfigOptions 点进去看,可以看到该类中提到的 64 KB 为数据溢写大小。
通过查看 TableConfigOptions 和 TableConfig ,可以发现一句很重要的话:Java has a maximum method length of 64 KB.
在 TableConfigOptions 中发现了修改这个值的配置项 table.generated-code.max-length
,int类型,且最大 64KB 。
在官网上(https://nightlies.apache.org/flink/flink-docs-release-1.13/docs/dev/table/config/)
找到修改语法:set table.generated-code.max-length=xxx;
因为 64KB 限制的存在,对该配置项做出增大的尝试都是失败告终。
在 flink 的 issue 里,看到这个限制源于: JVM 对方法的编译代码有 64KB 的限制[https://github.com/apache/flink/pull/16349]。
但同时发现,这个问题有解决办法:https://issues.apache.org/jira/browse/FLINK-23007。
待跟进。。。
2022.09.30
回头看了下这个问题,这次没有去翻源码,而是直接到 issue 里找到了几乎一模一样的问题描述,而且和我们的版本是一致的,打开environment 看,也是 sql 太长导致的这个问题。
https://issues.apache.org/jira/browse/FLINK-22903
顺着向下走,发现这个问题已被 Flink-23007 解决。
转到 Flink-23007 页面看下,看到 Fix Version/s:1.14.0,说明在1.14版本该问题被解决,同时在描述里看到:
解决办法是引入一个后置的基于 antlr4 的 java 类拆分器,来实现分析代码并拆分方法到多个方法的工作。
antlr4是一款基于Java开发的开源的语法分析器生成工具,广泛应用于DSL构建,语言词法语法解析,静态代码分析等领域。
至于第二句,去 1.14 的 CompileUtils 里看了下,发现和1.13没多大区别,看来在 CompileUtils里加入这个拆分器应该只是个建议。
在 1.14 的源码中,发现 flink/flink-table 下,多了个 flink-table-code-splitter 文件夹,看名字像是为了解决类似问题而实现的包。包中有这些类:
因为我们遇到的问题的根源是 Java 代码过长的问题,所以我们去 JavaCodeSplitter 类中看下:
发现注释写着:
——重写生成的 java 代码以使每个方法的长度变得更小从而可被编译。
正是我们遇到的问题:方法过长导致编译时就中止报错。
看一下代码:
split方法,很明显,如果代码小于等于maxMethodLength,就不做变动原样返回。
如果大于这个maxMethodLength这个阈值,就执行splitImpl方法。
我们查下有谁会用到这个方法,看看这个maxMethodLength会是什么值:
——GeneratedClass
在TableConfigOptions类里,继续去TableConfigOptions:
然后发现了眼熟的代码:
同时还发现下面还出现了对类成员变量的限制(先不管这个)。
回到 JavaCodeSplitter 看下怎么拆分的:splitImpl 方法。
总体是这三步,
- 对入参做校验
- 得到代码
- 对代码做三种rewrite
第三步的这三个类,点进去看,都引用了 antlr4 的包。
splitImpl 的相关源码不再赘述,点进各个类去看,应该是很好分析理解的。
至此,给个结论就是:1.13不行,1.14行。
当然,回到https://issues.apache.org/jira/browse/FLINK-23007页面,其实这个问题,还没有完全被解决。。。