SpringBoot项目集成Flink,通过java -jar 报错

通过Java代码连接Flink(1.19.1),插入和读取数据,在Windows的IDEA开发过程中一切正常,打包后到服务器上运行就报错,连接都正常,操作数据无法执行SQL语句,问题如下:

org.apache.flink.table.api.TableException: Failed to execute sql
Caused by: org.apache.flink.util.FlinkException: Failed to execute job 'collect'.
Caused by: java.lang.RuntimeException: org.apache.flink.runtime.client.JobInitializationException: Could not start the JobMaster.
Caused by: org.apache.flink.runtime.client.JobInitializationException: Could not start the JobMaster.
Caused by: java.util.concurrent.CompletionException: java.lang.RuntimeException: java.lang.ClassNotFoundException: org.apache.flink.api.common.ExecutionConfig
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: org.apache.flink.api.common.ExecutionConfig
Caused by: java.lang.ClassNotFoundException: org.apache.flink.api.common.ExecutionConfig
	at java.net.URLClassLoader.findClass(URLClassLoader.java:387) ~[?:1.8.0_333]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[?:1.8.0_333]
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) ~[?:1.8.0_333]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[?:1.8.0_333]
	at java.lang.Class.forName0(Native Method) ~[?:1.8.0_333]
	at java.lang.Class.forName(Class.java:348) ~[?:1.8.0_333]
	at org.apache.flink.util.InstantiationUtil$ClassLoaderObjectInputStream.resolveClass(InstantiationUtil.java:78) ~[flink-core-1.19.1.jar!/:1.19.1]
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2011) ~[?:1.8.0_333]
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1875) ~[?:1.8.0_333]
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2209) ~[?:1.8.0_333]
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1692) ~[?:1.8.0_333]
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:508) ~[?:1.8.0_333]
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:466) ~[?:1.8.0_333]
	at org.apache.flink.util.InstantiationUtil.deserializeObject(InstantiationUtil.java:539) ~[flink-core-1.19.1.jar!/:1.19.1]
	at org.apache.flink.util.InstantiationUtil.deserializeObject(InstantiationUtil.java:527) ~[flink-core-1.19.1.jar!/:1.19.1]
	at org.apache.flink.util.SerializedValue.deserializeValue(SerializedValue.java:67) ~[flink-core-1.19.1.jar!/:1.19.1]
	at org.apache.flink.runtime.scheduler.adaptivebatch.AdaptiveBatchSchedulerFactory.createInstance(AdaptiveBatchSchedulerFactory.java:150) ~[flink-runtime-1.19.1.jar!/:1.19.1]
	at org.apache.flink.runtime.jobmaster.DefaultSlotPoolServiceSchedulerFactory.createScheduler(DefaultSlotPoolServiceSchedulerFactory.java:121) ~[flink-runtime-1.19.1.jar!/:1.19.1]
	at org.apache.flink.runtime.jobmaster.JobMaster.createScheduler(JobMaster.java:379) ~[flink-runtime-1.19.1.jar!/:1.19.1]
	at org.apache.flink.runtime.jobmaster.JobMaster.<init>(JobMaster.java:356) ~[flink-runtime-1.19.1.jar!/:1.19.1]
	at org.apache.flink.runtime.jobmaster.factories.DefaultJobMasterServiceFactory.internalCreateJobMasterService(DefaultJobMasterServiceFactory.java:128) ~[flink-runtime-1.19.1.jar!/:1.19.1]
	at org.apache.flink.runtime.jobmaster.factories.DefaultJobMasterServiceFactory.lambda$createJobMasterService$0(DefaultJobMasterServiceFactory.java:100) ~[flink-runtime-1.19.1.jar!/:1.19.1]
	at org.apache.flink.util.function.FunctionUtils.lambda$uncheckedSupplier$4(FunctionUtils.java:112) ~[flink-core-1.19.1.jar!/:1.19.1]
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1604) ~[?:1.8.0_333]
	... 3 more

问题出现的原因

Spring Boot 的打包机制与 Flink 内部的类加载机制不兼容,导致了类加载器(ClassLoader)的冲突。Spring Boot 的可执行 Jar 包(Fat Jar)采用了一种特殊的类加载结构,而 Flink 在运行时默认使用的类加载器无法访问到这种结构下的依赖类,所以会报ClassNotFoundException异常。

这个问题通常不会在IDEA中直接运行出现,只会在使用 java -jar 命令运行打包后的 Spring Boot Jar 时发生。

关键因素 Spring Boot环境 Flink框架默认行为 冲突结果
打包结构 使用spring-boot-maven-plugin打包,依赖Jar位于BOOT-INF/lib/目录内 Flink的类加载逻辑(如InstantiationUtil)默认使用Thread.currentThread().getContextClassLoader()或ClassLoader.getSystemClassLoader() 系统类加载器(AppClassLoader)的类路径不包含BOOT-INF/lib/,因此找不到ExecutionConfig等类
类加载器隔离 Spring Boot的LaunchedURLClassLoader负责加载BOOT-INF/lib/和BOOT-INF/classes/下的类 Flink的InstantiationUtil.loadClass()等方法在反序列化或动态加载类时,如果未显式指定类加载器,可能回退到不正确的类加载器 。 当InstantiationUtil内部使用AppClassLoader时,无法加载由Spring Boot类加载器管理的Flink类。
触发场景 通常发生在打包后通过java -jar启动应用时 。 在IDE中运行时,类路径是平坦的,所有依赖都在系统类路径上,因此不会出错 。 开发与生产环境的不一致性导致问题。

Spring Boot 的可执行 Jar 结构:Spring Boot 通过 spring-boot-maven-plugin 插件打包时,会将所有应用依赖的 Jar 包(包括 flink-core, flink-java 等)重新打包,并放置在 BOOT-INF/lib/ 目录下。应用的类则位于 BOOT-INF/classes/。这种结构需要一个特殊的类加载器 LaunchedURLClassLoader(它是 URLClassLoader 的子类)来加载这些位于嵌套 Jar 中的类和资源。

Flink 的类加载行为:Flink 框架内部在序列化、反序列化或执行某些操作时,会使用特定的类加载器来加载类。如org.apache.flink.util.InstantiationUtil 类中的代码会尝试加载 ExecutionConfig。在某些场景下(如本地执行环境初始化),Flink 可能会使用 AppClassLoader(即系统类加载器)或 Thread.currentThread().getContextClassLoader() 来加载类

冲突的产生:当 Spring Boot 应用启动后,主线程的上下文类加载器是 Spring Boot 的 LaunchedURLClassLoader,然而,Flink 内部的某些代码(可能是由新创建的线程或内部逻辑触发)可能会错误地使用 AppClassLoader 或一个无法访问 BOOT-INF/lib/ 下 Jar 包的类加载器去加载 ExecutionConfig。由于 AppClassLoader 的类路径(Classpath)通常只包含 JRE 库和启动时指定的 Jar,它无法“看到”嵌套在 Spring Boot Jar 文件 BOOT-INF/lib/ 中的 flink-core.jar,因此就抛出了 ClassNotFoundException。

问题的解决办法

核心解决思路:确保Flink在加载其内部类时,使用的类加载器能够正确访问到位于BOOT-INF/lib/下的Jar包。

搜罗的解决办法 链接 我的实测情况
设置线程上下文类加载器 https://developer.aliyun.com/ask/527127 无效
调整打包插件配置 https://developer.aliyun.com/ask/600154?spm=a2c6h.12873639.article-detail.4.7c643276gnjRbE 无效
修改Flink工具类 https://blog.csdn.net/weixin_43039757/article/details/146046138 有效
版本兼容性 没有链接,从上述类型的解决办法的评论中看到的,我的版本从1.19降到1.15 有效

有效解决办法实际操作:

方法1. 修改Flink的工具类

  1. 查找flink-core-1.19.1.jar包中的工具类所在位置,复制工具类
    在这里插入图片描述
  2. 在项目下新建工具类,注意包名要与原来的一样,并将1中复制的内容粘贴过来,并修改以下内容
    在这里插入图片描述
  3. 保存后重新打包,再java -jar即可正常运行

方法2. 修改pom.xml中flink相关的包版本

  1. 找到项目中pom.xml引入flink的相关依赖,将version的位置修改为低版本,如1.15.3。
  2. 更新maven后重新打包,即可正常运行。
    修改版本号
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐