我们知道,在 JVM 中,一个类加载的过程大致分为加载、链接(验证、准备、解析)、初始化5个阶段。而我们通常提到类的加载,就是指利用类加载器(ClassLoader)通过类的全限定名来获取定义此类的二进制字节码流,进而构造出类的定义。

Flink 作为基于 JVM 的框架,在 flink-conf.yaml 中提供了控制类加载策略的参数 classloader.resolve-order,可选项有 child-first(默认)和 parent-first。本文来简单分析一下这个参数背后的含义。

parent-first 类加载策略

ParentFirstClassLoader 和 ChildFirstClassLoader 类的父类均为 FlinkUserCodeClassLoader 抽象类,先来看看这个抽象类,代码很短。

public abstract class FlinkUserCodeClassLoader extends URLClassLoader {public static final Consumer<Throwable> NOOP_EXCEPTION_HANDLER = classLoadingException -> {};private final Consumer<Throwable> classLoadingExceptionHandler;protected FlinkUserCodeClassLoader(URL[] urls, ClassLoader parent) {this(urls, parent, NOOP_EXCEPTION_HANDLER);}protected FlinkUserCodeClassLoader(URL[] urls,ClassLoader parent,Consumer<Throwable> classLoadingExceptionHandler) {super(urls, parent);this.classLoadingExceptionHandler = classLoadingExceptionHandler;}@Overrideprotected final Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {try {return loadClassWithoutExceptionHandling(name, resolve);} catch (Throwable classLoadingException) {classLoadingExceptionHandler.accept(classLoadingException);throw classLoadingException;}}protected Class<?> loadClassWithoutExceptionHandling(String name, boolean resolve) throws ClassNotFoundException {return super.loadClass(name, resolve);}
}

FlinkUserCodeClassLoader 继承自 URLClassLoader。因为 Flink App 的用户代码在运行期才能确定,所以通过 URL 在 JAR 包内寻找全限定名对应的类是比较合适的。而 ParentFirstClassLoader 仅仅是一个继承 FlinkUserCodeClassLoader 的空类而已。

static class ParentFirstClassLoader extends FlinkUserCodeClassLoader {    ParentFirstClassLoader(URL[] urls, ClassLoader parent, Consumer<Throwable> classLoadingExceptionHandler) {super(urls, parent, classLoadingExceptionHandler);}
}

这样就相当于 ParentFirstClassLoader 直接调用了父加载器的 loadClass() 方法。之前已经讲过,JVM 中类加载器的层次关系和默认 loadClass() 方法的逻辑由双亲委派模型(parents delegation model)来体现,复习一下含义:

如果一个类加载器要加载一个类,它首先不会自己尝试加载这个类,而是把加载的请求委托给父加载器完成,所有的类加载请求最终都应该传递给最顶层的启动类加载器。只有当父加载器无法加载到这个类时,子加载器才会尝试自己加载。

可见,Flink 的 parent-first 类加载策略就是照搬双亲委派模型的。也就是说,用户代码的类加载器是 Custom ClassLoader,Flink 框架本身的类加载器是 Application ClassLoader。用户代码中的类先由 Flink 框架的类加载器加载,再由用户代码的类加载器加载。但是,Flink 默认并不采用 parent-first 策略,而是采用下面的 child-first 策略,继续看。

child-first 类加载策略

我们已经了解到,双亲委派模型的好处就是随着类加载器的层次关系保证了被加载类的层次关系,从而保证了 Java 运行环境的安全性。但是在 Flink App 这种依赖纷繁复杂的环境中,双亲委派模型可能并不适用。例如,程序中引入的 Flink-Cassandra Connector 总是依赖于固定的 Cassandra 版本,用户代码中为了兼容实际使用的 Cassandra 版本,会引入一个更低或更高的依赖。而同一个组件不同版本的类定义有可能会不同(即使类的全限定名是相同的),如果仍然用双亲委派模型,就会因为 Flink 框架指定版本的类先加载,而出现莫名其妙的兼容性问题,如 NoSuchMethodError、IllegalAccessError 等。

鉴于此,Flink 实现了 ChildFirstClassLoader 类加载器并作为默认策略。它打破了双亲委派模型,使得用户代码的类先加载,官方文档中将这个操作称为"Inverted Class Loading"。代码仍然不长,录如下。

public final class ChildFirstClassLoader extends FlinkUserCodeClassLoader {private final String[] alwaysParentFirstPatterns;public ChildFirstClassLoader(URL[] urls,ClassLoader parent,String[] alwaysParentFirstPatterns,Consumer<Throwable> classLoadingExceptionHandler) {super(urls, parent, classLoadingExceptionHandler);this.alwaysParentFirstPatterns = alwaysParentFirstPatterns;}@Overrideprotected synchronized Class<?> loadClassWithoutExceptionHandling(String name,boolean resolve) throws ClassNotFoundException {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {// check whether the class should go parent-firstfor (String alwaysParentFirstPattern : alwaysParentFirstPatterns) {if (name.startsWith(alwaysParentFirstPattern)) {return super.loadClassWithoutExceptionHandling(name, resolve);}}try {// check the URLsc = findClass(name);} catch (ClassNotFoundException e) {// let URLClassLoader do it, which will eventually call the parentc = super.loadClassWithoutExceptionHandling(name, resolve);}}if (resolve) {resolveClass(c);}return c;}@Overridepublic URL getResource(String name) {// first, try and find it via the URLClassloaderURL urlClassLoaderResource = findResource(name);if (urlClassLoaderResource != null) {return urlClassLoaderResource;}// delegate to superreturn super.getResource(name);}@Overridepublic Enumeration<URL> getResources(String name) throws IOException {// first get resources from URLClassloaderEnumeration<URL> urlClassLoaderResources = findResources(name);final List<URL> result = new ArrayList<>();while (urlClassLoaderResources.hasMoreElements()) {result.add(urlClassLoaderResources.nextElement());}// get parent urlsEnumeration<URL> parentResources = getParent().getResources(name);while (parentResources.hasMoreElements()) {result.add(parentResources.nextElement());}return new Enumeration<URL>() {Iterator<URL> iter = result.iterator();public boolean hasMoreElements() {return iter.hasNext();}public URL nextElement() {return iter.next();}};}
}

核心逻辑位于 loadClassWithoutExceptionHandling() 方法中,简述如下:

  1. 调用 findLoadedClass() 方法检查全限定名 name 对应的类是否已经加载过,若没有加载过,再继续往下执行。

  2. 检查要加载的类是否以 alwaysParentFirstPatterns 集合中的前缀开头。如果是,则调用父类的对应方法,以 parent-first 的方式来加载它。

  3. 如果类不符合 alwaysParentFirstPatterns 集合的条件,就调用 findClass() 方法在用户代码中查找并获取该类的定义(该方法在 URLClassLoader 中有默认实现)。如果找不到,再 fallback 到父加载器来加载。

  4. 最后,若 resolve 参数为 true,就调用 resolveClass() 方法链接该类,最后返回对应的 Class 对象。

可见,child-first 策略避开了“先把加载的请求委托给父加载器完成”这一步骤,只有特定的某些类一定要“遵循旧制”。alwaysParentFirstPatterns 集合中的这些类都是 Java、Flink 等组件的基础,不能被用户代码冲掉。它由以下两个参数来指定:

classloader.parent-first-patterns.default,不建议修改,固定为以下这些值:

java.;
scala.;
org.apache.flink.;
com.esotericsoftware.kryo;
org.apache.hadoop.;
javax.annotation.;
org.slf4j;
org.apache.log4j;
org.apache.logging;
org.apache.commons.logging;
ch.qos.logback;
org.xml;
javax.xml;
org.apache.xerces;
org.w3c
  • classloader.parent-first-patterns.additional:除了上一个参数指定的类之外,用户如果有其他类以 child-first 模式会发生冲突,而希望以双亲委派模型来加载的话,可以额外指定(分号分隔)。

以上是关于 flink-conf.yaml 中提供的控制类加载策略的参数 classloader.resolve-order 含义的理解和分享,希望对大家有所启发和帮助~

原文链接:

https://www.jianshu.com/p/bc7309b03407


  福利来了  

Apache Flink 极客挑战赛

万众瞩目的第二届 Apache Flink 极客挑战赛来啦!本次大赛全面升级,重量级助阵嘉宾专业指导,强大的资源配置供你发挥创意,还有 30w 丰厚奖金等你带走~聚焦  Flink 与 AI 技术的应用实践,挑战疫情防控的世界级难题,你准备好了么?

(点击图片可了解更多大赛信息)

戳我报名!

双亲委派模型与 Flink 的类加载策略相关推荐

  1. 【Flink】双亲委派模型与Flink的类加载策略 child-first parent-first

    1.概述 转载:双亲委派模型与Flink的类加载策略 child-first parent-first 我记得我好像以前写过类似的文章,但是找不到了,转载一篇补充一下.. 2.类加载 我们知道,在JV ...

  2. Flink从入门到精通100篇(十)-双亲委派模型与 Flink 的类加载策略

    前言 我们知道,在 JVM 中,一个类加载的过程大致分为加载.链接(验证.准备.解析).初始化5个阶段.而我们通常提到类的加载,就是指利用类加载器(ClassLoader)通过类的全限定名来获取定义此 ...

  3. 面向对象回顾(静态变量、类加载机制/双亲委派模型、Object类的方法、类和对象区别)

    1. 静态变量存在什么位置? 方法区 2. 类加载机制,双亲委派模型,好处是什么? 某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务, ...

  4. 类加载器、双亲委派模型

    目录 1.简介 2.类和类加载器 3.双亲委派模型 3.1 启动类加载器: 3.2 扩展类加载器 3.3应用程序类加载器 3.4  类加载器的双亲委派模型(Parents Delegation Mod ...

  5. Java虚拟机:对象创建过程与类加载机制、双亲委派模型

    一.对象的创建过程: 1.对象的创建过程: 对象的创建过程一般是从 new 指令(JVM层面)开始的,整个创建过程如下: (1)首先检查 new 指令的参数是否能在常量池中定位到一个类的符号引用: ( ...

  6. 28 Java类的加载机制、什么是类的加载、类的生命周期、加载:查找并加载类的二进制数据、连接、初始化、类加载器、双亲委派模型、自定义类加载器

    28Java类的加载机制 28.1.什么是类的加载 28.2.类的生命周期 28.2.1.加载:查找并加载类的二进制数据 28.2.2.连接 28.2.3.初始化 28.3.类加载器 28.4.类的加 ...

  7. Android插件化开发基础之Java类加载器与双亲委派模型

    类加载器 Java虚拟机类加载过程是把Class类文件加载到内存,并对Class文件中的数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程. 在加载阶段,java虚拟机需 ...

  8. 【深入理解JVM】:类加载器与双亲委派模型

    转载自  [深入理解JVM]:类加载器与双亲委派模型 类加载器 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段" ...

  9. Tomcat类加载器为何违背双亲委派模型

    本文来说下Tomcat类加载器为何违背双亲委派模型 文章目录 什么是类加载机制 什么是双亲委派模型 如何破坏双亲委任模型 Tomcat的类加载器是怎么设计的 本文小结 什么是类加载机制 代码编译的结果 ...

  10. 深入理解 Tomcat(四)Tomcat 类加载器之为何违背双亲委派模型

    这是我们研究Tomcat的第四篇文章,前三篇文章我们搭建了源码框架,了解了tomcat的大致的设计架构, 还写了一个简单的服务器.按照我们最初订的计划,今天,我们要开始研究tomcat的几个主要组件( ...

最新文章

  1. Webpack 核心开发者 Sean Larkin 盛赞 Vue
  2. 使用SSH连接CentOS步骤
  3. setTimeout(fn,0)
  4. 如何获取登录token值_Token认证,如何快速方便获取用户信息
  5. 20190915:(leetcode习题)对称二叉树
  6. 后端 消息 转发_springCloud 后端使用webSocket向前端推送消息
  7. nRF51822 SPI 驱动 ADXL362
  8. MSF利用pcshare控住目标主机
  9. jsmind 线条_jsMind思维导图模式数据展示
  10. java坦克大战互相碰撞_加强版坦克大战(java版)
  11. ros 控制xbox_从提示框:在Windows中控制Xbox控制器,在夏天保持计算机凉爽以及DIY图书扫描装置...
  12. DMM数据管理能力成熟度模型简介
  13. 如何使用python获取Windows 10精美的桌面壁纸
  14. matlab 方差计算
  15. 视频教程免费分享:嵌入式stm32项目开发之心率检测仪的设计与实现
  16. MySQL触发器怎么写?
  17. 由于Rambler Group的诉讼,正在搜寻Nginx的办公室。 投诉人新闻服务确认诉讼
  18. 计算机协会发言稿范文,科技节的发言稿范文(精选5篇)
  19. uni-app小程序滑动到顶部固定
  20. JS: || 和 ??

热门文章

  1. SQL:pgsql中查询某字段不等于的数据
  2. Javascript:访问和设置CSS属性
  3. 全国计算机等级考试在线报名,全国计算机等级考试网上报考具体流程
  4. ios ipad适配_安卓和iOS的APP在开发时有哪些区别?
  5. python通讯录运用的知识点_Python实现通讯录功能
  6. 斯坦福大学深度学习公开课cs231n学习笔记(7)神经网络防止数据过拟合:损失函数和正则化
  7. node.js(一)基础介绍
  8. 辗转相除法 两个数的最大公约数
  9. 前端小知识-html5
  10. python 第一课作用