热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【Hudi数据湖应用】Flink作业同名类强转异常ClassCastException修复

一、异常描述 近日升级到hudi 0.11后,在flink应用中遭遇了一个神级异常:java.lang.ClassCastException: org.apache.hud
一、异常描述

近日升级到hudi 0.11后,在flink应用中遭遇了一个神级异常:java.lang.ClassCastException: org.apache.hudi.common.fs.HoodieWrapperFileSystem cannot be cast to org.apache.hudi.common.fs.HoodieWrapperFileSystem。
没看错吧?同名类转换失败?揉揉眼睛,逐字符再看一次,的确是同名类。同类对象转换还失败了?真是活久见。

2022-05-30 14:40:27
java.lang.ClassCastException: org.apache.hudi.common.fs.HoodieWrapperFileSystem cannot be cast to org.apache.hudi.common.fs.HoodieWrapperFileSystemat org.apache.hudi.io.storage.HoodieParquetWriter.<init>(HoodieParquetWriter.java:72)at org.apache.hudi.io.storage.HoodieFileWriterFactory.newParquetFileWriter(HoodieFileWriterFactory.java:84)at org.apache.hudi.io.storage.HoodieFileWriterFactory.newParquetFileWriter(HoodieFileWriterFactory.java:70)at org.apache.hudi.io.storage.HoodieFileWriterFactory.getFileWriter(HoodieFileWriterFactory.java:54)at org.apache.hudi.io.HoodieCreateHandle.<init>(HoodieCreateHandle.java:101)at org.apache.hudi.io.HoodieCreateHandle.<init>(HoodieCreateHandle.java:80)at org.apache.hudi.io.FlinkCreateHandle.<init>(FlinkCreateHandle.java:67)at org.apache.hudi.io.FlinkCreateHandle.<init>(FlinkCreateHandle.java:60)at org.apache.hudi.client.HoodieFlinkWriteClient.getOrCreateWriteHandle(HoodieFlinkWriteClient.java:479)at org.apache.hudi.client.HoodieFlinkWriteClient.upsert(HoodieFlinkWriteClient.java:143)at org.apache.hudi.sink.StreamWriteFunction.lambda$initWriteFunction$1(StreamWriteFunction.java:184)at org.apache.hudi.sink.StreamWriteFunction.lambda$flushRemaining$7(StreamWriteFunction.java:461)at java.util.LinkedHashMap$LinkedValues.forEach(LinkedHashMap.java:608)at org.apache.hudi.sink.StreamWriteFunction.flushRemaining(StreamWriteFunction.java:454)at org.apache.hudi.sink.StreamWriteFunction.endInput(StreamWriteFunction.java:151)at org.apache.hudi.sink.common.AbstractWriteOperator.endInput(AbstractWriteOperator.java:48)at org.apache.flink.streaming.runtime.tasks.StreamOperatorWrapper.endOperatorInput(StreamOperatorWrapper.java:91)at org.apache.flink.streaming.runtime.tasks.RegularOperatorChain.endInput(RegularOperatorChain.java:100)at org.apache.flink.streaming.runtime.io.StreamOneInputProcessor.processInput(StreamOneInputProcessor.java:68)at org.apache.flink.streaming.runtime.tasks.StreamTask.processInput(StreamTask.java:496)at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.runMailboxLoop(MailboxProcessor.java:203)at org.apache.flink.streaming.runtime.tasks.StreamTask.runMailboxLoop(StreamTask.java:809)at org.apache.flink.streaming.runtime.tasks.StreamTask.invoke(StreamTask.java:761)at org.apache.flink.runtime.taskmanager.Task.runWithSystemExitMonitoring(Task.java:958)at org.apache.flink.runtime.taskmanager.Task.restoreAndInvoke(Task.java:937)at org.apache.flink.runtime.taskmanager.Task.doRun(Task.java:766)at org.apache.flink.runtime.taskmanager.Task.run(Task.java:575)at java.lang.Thread.run(Thread.java:748)

二、异常分析

2.1 类的判同方法

一般而言&#xff0c;ClassCastException是不同类之间的对象强行转换时才会引起的。那么在java里&#xff0c;怎么判断一个类是否相同呢&#xff1f;
我们知道&#xff0c;在java里&#xff0c;万物皆对象&#xff0c;我们开发的每一个类&#xff0c;一般情况下&#xff0c;运行时都有唯一一个对应的Class类对象。这个类对象在类首次被使用时由java虚拟机加载、创建。我们看Class类的equal方法源码&#xff0c;两个类对象是否相同&#xff0c;取决于是否为同一个对象引用。

public boolean equals(Object obj) {return (this &#61;&#61; obj);}

很明显&#xff0c;类对象由谁创建和持有&#xff0c;就影响到了类是否相同的判断及类对象强转。而这就涉及到了jvm原理和类的加载机制。

2.2 类双亲委派加载机制

简单而言&#xff0c;jvm运行时会有一个专属的内存区域&#xff08;方法区或meta space)存放类的信息。类信息由jvm通过类加载器ClassLoader来加载。熟悉jvm原理的人都知道&#xff0c;默认情况下&#xff0c;java虚拟机使用双亲委派机制进行类加载。

双亲委派机制简述如下&#xff1a;

1、如果一个类加载器收到了类加载请求&#xff0c;它并不会自己先去加载&#xff0c;而是把这个请求委托给父类的加载器去执行。

2、如果父类加载器还存在其父类加载器&#xff0c;则进一步向上委托&#xff0c;依次递归请求最终将到达顶层的启动类加载器。

3、如果父类加载器可以完成类加载任务&#xff0c;就成功返回&#xff0c;倘若父类加载器无法完成此加载任务&#xff0c;子加载器才会尝试自己去加载&#xff0c;这就是双亲委派模式。
在这里插入图片描述
由此可以看出&#xff0c;在双亲委派机制下&#xff0c;可以保证一个类只会被加载一次&#xff0c;避免类的重复加载。

2.3 两个class对象判同的必要条件

前面我们通过源码可以知道&#xff0c;类对象判同是通过类对象的指针是否相同来判断的。而类对象通过双亲委派机制由Classloader加载、创建。而类是通过全类名&#xff08;Class.forName&#xff09;来寻找、加载的。
由此可以推导出&#xff0c;在JVM中表示两个class对象是否是同一个类的两个必要条件&#xff1a;

1、类的全限制名一致&#xff08;包名&#43;类名&#xff09;。

2、加载这个类的ClassLoader必须相同。

2.4 Flink的类加载机制

由于我们遇到的是同名类强转异常&#xff0c;可以肯定我们类的全限定名是一致的。那么问题就是出在类的加载器身上。
我们知道&#xff0c;flink有一个配置项&#xff0c;classloader.resolve-order&#xff0c;可以配置类的加载顺序。classloader.resolve-order的默认配置是child-first&#xff0c;这一配置打破了java默认的双亲委派机制。
child-first配置对应的类加载器是ChildFirstClassLoader&#xff0c;这个加载器除了指定的系统类、java lang、flink源等基础类是遵守双亲委派由父加载器加载&#xff0c;其他的类是直接由ChildFirstClassLoader直接加载。这样做的一个好处是&#xff0c;当用户依赖于flink依赖有冲突时&#xff0c;用户可以优先使用自己包内在类进行加载。
在这里插入图片描述

2.5 ChildFirstClassLoader核心源码解析

ChildFirstClassLoader的构造器需要指定jar包资源urls、父加载器、父加载器加载的类模式、加载失败处理逻辑。
父加载器加载的类范围由assloader.parent-first-patterns.default指定&#xff0c;符合规则的由父加载器加载&#xff0c;否则由子加载器进行加载&#xff08;loadClassWithoutExceptionHandling方法&#xff09;。子加载器寻找jar包资源时&#xff08;getResource方法&#xff09;&#xff0c;也优先寻找用户资源&#xff0c;再寻找父加载器的资源。
assloader.parent-first-patterns.default默认为&#xff1a;

java.;scala.;org.apache.flink.;com.esotericsoftware.kryo;org.apache.hadoop.;javax.annotation.;org.xml;javax.xml;org.apache.xerces;org.w3c;org.rocksdb。

ChildFirstClassLoader核心源码&#xff1a;

public final class ChildFirstClassLoader extends FlinkUserCodeClassLoader {/*** The classes that should always go through the parent ClassLoader. This is relevant for Flink* classes, for example, to avoid loading Flink classes that cross the user-code/system-code* barrier in the user-code ClassLoader.*/private final String[] alwaysParentFirstPatterns;public ChildFirstClassLoader(URL[] urls,ClassLoader parent,String[] alwaysParentFirstPatterns,Consumer<Throwable> classLoadingExceptionHandler) {super(urls, parent, classLoadingExceptionHandler);this.alwaysParentFirstPatterns &#61; alwaysParentFirstPatterns;}&#64;Overrideprotected Class<?> loadClassWithoutExceptionHandling(String name, boolean resolve)throws ClassNotFoundException {// First, check if the class has already been loadedClass<?> c &#61; findLoadedClass(name);if (c &#61;&#61; 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 &#61; findClass(name);} catch (ClassNotFoundException e) {// let URLClassLoader do it, which will eventually call the parentc &#61; super.loadClassWithoutExceptionHandling(name, resolve);}} else if (resolve) {resolveClass(c);}return c;}&#64;Overridepublic URL getResource(String name) {// first, try and find it via the URLClassloaderURL urlClassLoaderResource &#61; findResource(name);if (urlClassLoaderResource !&#61; null) {return urlClassLoaderResource;}// delegate to superreturn super.getResource(name);}&#64;Overridepublic Enumeration<URL> getResources(String name) throws IOException {// first get resources from URLClassloaderEnumeration<URL> urlClassLoaderResources &#61; findResources(name);final List<URL> result &#61; new ArrayList<>();while (urlClassLoaderResources.hasMoreElements()) {result.add(urlClassLoaderResources.nextElement());}// get parent urlsEnumeration<URL> parentResources &#61; getParent().getResources(name);while (parentResources.hasMoreElements()) {result.add(parentResources.nextElement());}return new Enumeration<URL>() {Iterator<URL> iter &#61; result.iterator();public boolean hasMoreElements() {return iter.hasNext();}public URL nextElement() {return iter.next();}};}static {ClassLoader.registerAsParallelCapable();}}

2.6 flink默认加载机制的衍生问题

由于flink默认使用ChildFirstClassLoader进行类加载&#xff0c;打破了源生的类加载机制&#xff0c;这可能会导致父加载类加载过的类&#xff0c;会被子加载器重复加载&#xff0c;进而导致同名类判同异常&#xff0c;影响对象强转&#xff0c;导致运行时出现同名类强转异常&#xff0c;抛ClassCastException。
它也会导致类的继承关系判断失效&#xff0c;导致子类转父类失败。因为父子类被不同的类加载器加载&#xff0c;同名父类被判断为不同类。异常示例如下&#xff1a;

Cannot cast org.apache.hudi.hive.HiveSyncConfig to org.apache.hudi.hive.replication.GlobalHiveSyncConfig
java.lang.ClassCastException: Cannot cast org.apache.hudi.hive.HiveSyncConfig to org.apache.hudi.hive.replication.GlobalHiveSyncConfig

三、异常解决方案

通过上述分析&#xff0c;在flink中遇到的同名类强转异常是由类加载器引起的。flink默认情况下&#xff0c;使用ChildFirstClassLoader进行加载。ChildFirstClassLoader加载类时&#xff0c;也优先使用用户的资源。一般情况下&#xff0c;这个机制是可以良好运行的。但有时也会出现同名类强转异常。这时在排除用户jar与flink运行依赖冲突后&#xff0c;若异常依然存在&#xff0c;可以修改flink的配置(conf/flink-conf.yaml)&#xff0c;将classloader.resolve-order改为parent-first&#xff0c;这样类就只会加载一次&#xff0c;避免重复加载出现强转异常。


推荐阅读
  • Flowable 流程图路径与节点展示:已执行节点高亮红色标记,增强可视化效果
    在Flowable流程图中,通常仅显示当前节点,而路径则需自行获取。特别是在多次驳回的情况下,节点可能会出现混乱。本文重点探讨了如何准确地展示流程图效果,包括已结束的流程和正在执行的流程。具体实现方法包括生成带有高亮红色标记的图片,以增强可视化效果,确保用户能够清晰地了解每个节点的状态。 ... [详细]
  • 在本地环境中部署了两个不同版本的 Flink 集群,分别为 1.9.1 和 1.9.2。近期在尝试启动 1.9.1 版本的 Flink 任务时,遇到了 TaskExecutor 启动失败的问题。尽管 TaskManager 日志显示正常,但任务仍无法成功启动。经过详细分析,发现该问题是由 Kafka 版本不兼容引起的。通过调整 Kafka 客户端配置并升级相关依赖,最终成功解决了这一故障。 ... [详细]
  • 本文探讨了如何在 Java 中将多参数方法通过 Lambda 表达式传递给一个接受 List 的 Function。具体分析了 `OrderUtil` 类中的 `runInBatches` 方法及其使用场景。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • Spring框架中枚举参数的正确使用方法与技巧
    本文详细阐述了在Spring Boot框架中正确使用枚举参数的方法与技巧,旨在帮助开发者更高效地掌握和应用枚举类型的数据传递,适合对Spring Boot感兴趣的读者深入学习。 ... [详细]
  • 深入探索HTTP协议的学习与实践
    在初次访问某个网站时,由于本地没有缓存,服务器会返回一个200状态码的响应,并在响应头中设置Etag和Last-Modified等缓存控制字段。这些字段用于后续请求时验证资源是否已更新,从而提高页面加载速度和减少带宽消耗。本文将深入探讨HTTP缓存机制及其在实际应用中的优化策略,帮助读者更好地理解和运用HTTP协议。 ... [详细]
  • 本文将继续探讨 JavaScript 函数式编程的高级技巧及其实际应用。通过一个具体的寻路算法示例,我们将深入分析如何利用函数式编程的思想解决复杂问题。示例中,节点之间的连线代表路径,连线上的数字表示两点间的距离。我们将详细讲解如何通过递归和高阶函数等技术实现高效的寻路算法。 ... [详细]
  • 经过两天的努力,终于成功解决了半平面交模板题POJ3335的问题。原来是在`OnLeft`函数中漏掉了关键的等于号。通过这次训练,不仅加深了对半平面交算法的理解,还提升了调试和代码实现的能力。未来将继续深入研究计算几何的其他核心问题,进一步巩固和拓展相关知识。 ... [详细]
  • Java中不同类型的常量池(字符串常量池、Class常量池和运行时常量池)的对比与关联分析
    在研究Java虚拟机的过程中,笔者发现存在多种类型的常量池,包括字符串常量池、Class常量池和运行时常量池。通过查阅CSDN、博客园等相关资料,对这些常量池的特性、用途及其相互关系进行了详细探讨。本文将深入分析这三种常量池的差异与联系,帮助读者更好地理解Java虚拟机的内部机制。 ... [详细]
  • 在Kohana 3框架中,实现最优的即时消息显示方法是许多开发者关注的问题。本文将探讨如何高效、优雅地展示flash消息,包括最佳实践和技术细节,以提升用户体验和代码可维护性。 ... [详细]
  • 本文深入解析了Java 8并发编程中的`AtomicInteger`类,详细探讨了其源码实现和应用场景。`AtomicInteger`通过硬件级别的原子操作,确保了整型变量在多线程环境下的安全性和高效性,避免了传统加锁方式带来的性能开销。文章不仅剖析了`AtomicInteger`的内部机制,还结合实际案例展示了其在并发编程中的优势和使用技巧。 ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • Java Socket 关键参数详解与优化建议
    Java Socket 的 API 虽然被广泛使用,但其关键参数的用途却鲜为人知。本文详细解析了 Java Socket 中的重要参数,如 backlog 参数,它用于控制服务器等待连接请求的队列长度。此外,还探讨了其他参数如 SO_TIMEOUT、SO_REUSEADDR 等的配置方法及其对性能的影响,并提供了优化建议,帮助开发者提升网络通信的稳定性和效率。 ... [详细]
author-avatar
秦schueler
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有