Flink 类加载机制介绍时间:2022-02-28 00:55:31- **类加载分类** Java Classpath 类加载:Java 的通用类路径,包括 JDK 库,以及 Flink /lib 目录中的所有代码(Apache Flink 的 类和一些依赖),这些都是通过 AppClassLoader 加载 Flink Plugin 组件类加载:Flink 的 /plugins 目录下的插件代码,Flink 的插件机制会在启动时动态加载它们 动态加载用户代码类:通过 FlinkUserCodeClassLoader 动态加载和卸载提交作业的 JAR 文件中的所有类(通过 REST、CLI、Web UI 提交的程序类) - **加载方式** 通用规则,每当Flink进程先启动,之后job提交时,job相关的类都是动态加载的。 如果Flink进程与job或应用程序一起启动,或者应用代码启动Flink组件(JobManager, TaskManager等),这时所有job的类存在于Java的classpath下。 - **不同部署模式的一些细节信息:** **Session模式(Standalone/Yarn/Kubernetes):** 当Flink Session集群启动时,JobManager和TaskManager由Java classpath中的Flink框架类(Flink framework classes)进行启动加载。而通过session提交(REST或命令行方式)的job或应用程序由FlinkUserCodeClassLoader进行加载。 **Per-Job模式(已弃用)(Yarn):** 当前只有Yarn支持Per-Job模式。默认情况下,Flink集群运行在Per-Job模式下时会将用户的jar文件包含在系统的classpath中。 这种模式可以由yarn.classpath.include-user-jar 参数控制。 当该参数设定为DISABLED时,Flink会将用户jar文件含在用户的classpath中,并由FlinkUserCodeClassLoader进行动态加载。 **Application模式(Standalone/Yarn/Kubernetes):** 当Application模式的Flink集群基于Standalone或Kubernetes方式运行时,用户jar文件(启动命令指定的jar文件和Flink的usrlib目录中的jar包)会由FlinkUserCodeClassLoader进行动态加载。 当Flink集群以Application模式运行时,用户jar文件(启动命令指定的jar文件和Flink的usrlib目录中的jar包)默认情况下会包含在系统classpath(AppClassLoader)。与Per-Job模式相同,当yarn.classpath.include-userjar设置为DISABLED时,Flink会将用户jar文件含在用户的classpath中,并由FlinkUserCodeClassLoader进行动态加载。 - **倒置类加载(Inverted Class Loading)和ClassLoader解析顺序** **动态类加载的层次结构涉及两种ClassLoader:** (1)Java的application classloader,包含classpath中的所有类; (2)动态的plugin/user code classloader,用来加载插件代码或用户代码的jar文件。动态的ClassLoader将应用程序classloader作为parent。 默认情况下Flink会倒置类加载顺序,首先Flink会查找动态类加载器,如果该类不属于动态加载的代码时才会去查找其parent(application classloader)。 ***倒置类加载的好处:*** 在于插件和job可以使用与Flink核心不同的库版本,尤其在使用不同版本的库从而出现不兼容的情况下。这种机制可以帮助避免常见的类似 IllegalAccessError 或NoSuchMethodError的依赖冲突错误。代码的不同部分会有独立的拷贝(Flink内核及它的不同依赖包可使用与用户代码或插件代码不同的拷贝),多数情况下这种方式可以正常运行,并且不需要用户进行额外配置。 对于用户代码的类加载,您可以通过调整Flink的classloader.resolve-order配置将ClassLoader解析顺序还原至Java的默认模式(从Flink默认的child-first调整为parent-first)。 请注意由于有些类在Flink内核与插件或用户代码间共享,它们总是以parent-first方式进行解析的。这些类相关的包通过classloader.parent-first-patterns-default和classloader.parent-first-patterns-additional进行配置。如果需要新添加parent-first 方式的包,请调整classloader.parent-first-patterns-additional 配置选项。 - **类冲突处理** 实际开发中可能会因为类加载出现 ClassNotFound 或者 NoSuchMethodError 和 NoSuchFieldError 等异常。这种情况可能是因为你的 jar 包中的版本和集群的版本不一致,优先加载了集群的 class。在不改动集群默认配置的情况下,同时也防止对其他任务的影响,可以考虑对项目中引入的冲突类作 shade。如 com.typesafe 的类发生冲突,maven 项目加入如下配置: ```xml org.apache.maven.pluginsmaven-shade-pluginpackageshadecom.typesafecom.zhisheng.com.typesafe ``` 项目打包后,就会自动把引入和源码中相关的 com.typesafe 开头的类修改为 com.zhisheng.com.typesafe,由于类的路 径不同,类加载器在加载用户 jar 中的类时就不会与集群中的其他版本冲突。 - **卸载动态加载的类** 当类被加载,连接和初始化后,它的生命周期就开始了。当代表类的Class对象不在被引用时,即不可触及时,Class对象就会结束生命周期,类在方法区内的数据也会被卸载,从而结束类的生命周期。