Flink-HBase生产问题排查:NoClassDefFoundError

作者:大大大大晴天️日期:2026/6/10

一、背景与问题

我们生产环境上有一个Flink实时作业近期出现写入 HBase 失败,日志频繁打印Exception日志,但作业的运行状态却一直健康正常;进行应急手动重启作业后恢复正常,HBase正常写入,未再复现。

环境信息:Flink(1.16)、HBase(2.4)、Kafka(2.8)

作业的计算链路DAG大致如下:

算子链路:Source → Filter/FlatMap → Process → HBaseSink

二、问题排查

此Flink作业是一个Flink-Java作业,用户自定义实现消费加工逻辑。

排查Flink作业的运行指标监控,发现各指标均正常(内存、反压、吞吐、checkpoint等)。

排查Flink作业异常日志,总结如下:

层级异常信息算子位置
最外层try-catch自定义打印Exception日志Filter/FlatMap
中间层Could not forward element to next operatorProcess
内层RetriesExhaustedWithDetailsException: Failed 1 actionHBaseSink
根因NoClassDefFoundError: Could not initialize class org.apache.hadoop.hbase.ipc.RemoteWithExtrasException$ClassLoaderHolderHBase Client

根据HBaseSinkException堆栈信息指引,Flink的Hbase-Client一直在尝试连接Rowkey所在HBase表region的一个RegionServer,接着发现该RegionServer最近重启过,而Flink并没有失败重试刷新缓存连接Meta表获取表Region信息。

另外,排查日志过程中发现此作业并不是所有HBase写入都失败,只有遇到HBase服务端响应异常时才抛出此异常堆栈。

最后我们review了此作业的Java代码,发现作业在最外层Filter/FlatMap使用try-catch整个包起来,而在catch代码块仅打印异常日志,没有增加异常处理。

1###简单示例
2public void flatMap(String value, Collector<OUT> out) {
3    try {
4        OUT result = transform(value);
5        out.collect(result);  //  关键:collect 也在 try 块内
6    } catch (Exception e) {
7        LOG.error("flatmap处理异常: {}", e.getMessage(), e);
8        // 没有 throw,没有 out.collect,什么都没做
9    }
10}

三、问题分析

查看作业DAG执行计划,这四个算子被 chain 在一起,意味着它们运行在同一个线程中,通过同步方法调用串联。Chain 内部不是消息队列,而是直接方法调用栈嵌套。

1┌─────────────────── 单线程(Task Thread)───────────────────┐
2                                                            
3  source.emitRecord()                                       
4                                                           
5        output.collect()                                   
6  flatmap.processElement()                                  
7                                                           
8        output.collect()                                   
9  process.processElement()                                  
10                                                           
11        output.collect()                                   
12  hbaseSink.invoke()    异常原点                            
13                                                            
14└────────────────────────────────────────────────────────────┘

异常从 hbaseSink 抛出后,沿着 Java 调用栈逐层向上 :

1线程调用栈(从底向上):
2
3TaskThread.run()
4 └─ StreamTask.processInput()
5     └─ StreamOneInputProcessor.processElement()
6         └─ ChainingOutput.collect()               flatmap  output
7             └─ FlatMapOperator.processElement()
8                 └─ ChainingOutput.collect()       process  output  
9                     └─ ProcessOperator.processElement()
10                         └─ ChainingOutput.collect()   sink  output
11                             └─ StreamSink.invoke()
12                                 └─ HBaseSinkFunction.invoke()
13                                     └─ mutator.mutate(put)
14                                         └─ 💥 NoClassDefFoundError

异常抛到Filter/FlatMap最外层时,被try-catch捕获,仅打印日志,这种处理会导致数据静默丢失,且同时阻断 HBase 重试和 Flink 自动重启机制,符合生产现象。

Flink 的 exactly-once / at-least-once 保障依赖一个核心前提:异常必须向上抛出,让框架感知到失败,才能触发 checkpoint 回滚和数据重放。

1正常容错链路:
2异常抛出  Task FAILED  JobManager 感知  触发 restart-strategy
3 从最近 checkpoint 恢复  source 回退 offset  数据重新消费
4
5 catch 吞掉后:
6异常打印日志  Task 继续 RUNNING  checkpoint 正常  offset 提交
7 数据永久丢失,无法恢复

到这里整个关系链就清晰了,接下来只要找到HBaseSinkException的NoClassDefFoundError问题点就闭环了。

四、根因分析

Flink作业日志中的根因异常是NoClassDefFoundError: Could not initialize class,不是ClassNotFoundException,这意味着类已经被找到,但静态初始化块执行失败。

根据源码定位,RemoteWithExtrasException$ClassLoaderHolder是 HBase client 中用于反序列化远程异常的内部类,其核心逻辑如下:

1// hbase-client: org.apache.hadoop.hbase.ipc.RemoteWithExtrasException
2private static class ClassLoaderHolder {
3    private static final ClassLoader CLASS_LOADER = initClassLoader();
4
5    private static ClassLoader initClassLoader() {
6        // 尝试获取当前线程上下文类加载器或系统类加载器
7        ClassLoader cl = Thread.currentThread().getContextClassLoader();
8        if (cl == null) {
9            cl = ClassLoader.getSystemClassLoader();
10        }
11        return cl;
12    }
13}

这个类在 HBase RPC 响应中遇到异常时被触发,用于加载远程异常类进行反序列化,异常触发路径如下:

1Flink TaskManager
2   HBase Client Put/Get
3     RPC 调用 RegionServer
4       RegionServer 返回异常响应
5         Client 尝试反序列化异常
6           加载 ClassLoaderHolder(静态初始化)
7             失败  NoClassDefFoundError

根据 JVM 规范,如果一个类的静态初始化抛出异常,JVM 会将该类标记为erroneous状态,后续任何对该类的引用都直接抛NoClassDefFoundError,不会重试初始化。这个标记是per-ClassLoader的,只有销毁 ClassLoader(重启 Task/JVM)才能重置。

直接原因:HBase Client 内部类RemoteWithExtrasException$ClassLoaderHolder的静态初始化失败,被 JVM 永久标记为 erroneous 状态。后续该 TaskManager 上所有 HBase 写入操作均触发NoClassDefFoundError,无法自愈,直至作业重启创建新的 ClassLoader。

根本原因:时序竞态问题--HBase RegionServer 瞬态故障(GC 停顿/网络抖动/Region 迁移)恰好与 Flink Task ClassLoader 生命周期切换窗口重叠,导致 HBase Client 后台重试线程在 ClassLoader 处于异常状态时首次触发了ClassLoaderHolder的静态初始化。

以下场景中,Flink 会切换或关闭 Task 的 ClassLoader:

  • Checkpoint 触发的 async snapshot
  • Task 取消/Failover(另一个 Task 失败触发整体重启)
  • TaskManager Slot 释放

五、优化与总结

  1. 修改 FlatMap 代码,在catch代码块中正常抛出Exception,让Flink框架感知到,能够触发容错重启机制
  2. Flink 配置增加 HBase 包 parent-first 加载策略,避免 ClassLoader 生命周期竞态
  3. 增加Flink作业异常日志关键字告警,及时感知并干预

本次故障为 HBase RegionServer 瞬态异常与 Flink 类加载器生命周期竞态叠加导致的低概率问题,重启可恢复。核心风险在于作业代码中 try-catch 吞异常的写法,会在常规 IO 异常场景下造成静默数据丢失。整改重点为修复代码异常处理逻辑、调整类加载策略、补齐监控告警。


Flink-HBase生产问题排查:NoClassDefFoundError》 是转载文章,点击查看原文


相关推荐


别再把“做个H5”挂嘴边了:这个词,官方压根就没有定义过
知航驿站2026/6/2

别再把“H5”当正式术语了 前言 做前端这些年,我一直觉得中文互联网里有个词特别有意思,就是“H5”。 这个词几乎人人都在用。产品说“做个 H5”,运营说“我要一个 H5 活动页”,甲方也会说“你们能不能先出个 H5 版本”。说得多了,很多人就默认:这一定是个很正式、很标准、很官方的技术词。 但真要较真一点看,这事其实不是这样。 HTML5 当然是标准里的正式说法,H5 却不是一个被官方单独定义出来、专门指代“活动页”“移动端网页”“营销页面”的术语。今天大家口中的“H5”,更像是中文互联网行业


构建无障碍组件之Toolbar Pattern
anOnion2026/5/25

Toolbar Pattern 详解:构建无障碍的工具栏组件 Toolbar(工具栏)是一种用于组合一组控件的容器,例如按钮、菜单按钮或复选框。本文基于 W3C WAI-ARIA Toolbar Pattern 规范,详解如何构建无障碍的工具栏组件。 一、Toolbar 的定义与核心概念 1.1 什么是 Toolbar Toolbar 是一种控件分组容器,具有以下特征: 将一组相关控件(按钮、菜单、复选框等)视觉上分组 通过 role="toolbar" 向屏幕阅读器用户传达分组的存在和目的


Android 窗口容器树(一)—— 窗口和窗口容器树
无限进化2026/5/4

窗口容器树系列文章: Android 窗口容器树(一)—— 窗口和窗口容器树 Android 窗口容器树(二)—— 窗口容器树的构建 1. 什么是窗口? 在 Android 中,窗口不是 View,也不是某个单独的界面控件,而是系统层面对一块可显示 UI 内容的抽象管理单元。 它至少有 3 个核心特征: 它有独立的 WindowManager.LayoutParams,用来描述类型、位置、大小、标志位等。 它最终会对应到 SurfaceControl / Surface,并交给 Surfa


【从0开始学设计模式-11| 外观模式】
我爱cope2026/4/24

概念 外观模式(Facade Pattern) 是一种使用频率非常高的结构型设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便。 定义: 为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 外观模式又称为门面模式,它是一种对象结构型模式。外观模式是迪米特法则的一种具体实现,通过引入一个新的外观角色可以降低原有系统的复杂度,同时降低客户类与子


VPS 买回来第一天该干什么?我的开机必做清单
小墨同学boy2026/4/16

很多人买来第一台VPS可能不知道干什么,是到手之后直接装宝塔,然后部署一个WordPress 博客丢上去跑了两天。可能过几天你就会发现 auth.log 里全是暴力破解记录,SSH 端口没有改还是默认的 22,root 密码没有做修改还是简单的 8 数字。最离谱的是你没有一开始测试号,最后发现那台机器的网络在晚高峰根本不能用,到最后可以部署完了才发现的,前面装的东西全白费功夫。 所以我才要写这篇文章,介绍我的流程:新机器到手,不要一上来就按照服务,先按照我的固定流程把去设置一遍服务器。整个过程其


# KubeBlocks for MSSQL 高可用实现
小猿姐2026/4/8

背景 Microsoft SQL Server(MSSQL)是由微软开发的一款关系型数据库管理系统。最初仅支持在 Windows 平台上运行,自 2017 版本起开始支持 Linux 系统,这一变化为 MSSQL 的容器化部署提供了可能。 MSSQL 提供了名为 Availability Group(可用性组,下文简称AG) 的多数据库复制管理特性,该特性支持在多个节点上实现数据库的多副本冗余,从而提升数据可靠性和服务连续性。在 Windows 平台上,MSSQL 通过与 Windows Ser


AI时代,我们的任务不应沉溺于与 AI 聊天 - 🤔 从“对话式编程”迈向“数字软件工厂”
阿文WUTAI感话2026/3/31

2026年,AI辅助开发的正确打开方式 在 2026 年的今天,研发工程师已经意识到,AI 辅助开发不应只是零散的提示词,而是一套 有标准、有性能、有角色、有流程 的系统工程 。通过 OpenSpec、Everything Claude Code (ECC)、gstack 以及新增的 superpowers 的深度协同,我们可以构建起一套现代化的"数字软件工厂",让研发协作工作流与状态流转更加符合项目的确定性需求 一、核心概念:先对齐术语,避免鸡同鸭讲 要驾驭这套工厂,首先需要对齐底层的专业概


家政服务小程序预约上门服务维修保洁上门服务在线派单技师入驻-ym7K
2601_952013762026/3/22

一、后台管理端核心功能 1. 系统基础配置 提供基础设置、店铺管理、家政管理三大核心配置入口,支撑系统整体运行。支持家政分类管理,可灵活划分保洁、月嫂、家电维修等服务类型。 2. 家政服务发布与管理 发布家政服务: 选择对应家政分类,填写服务标题。配置所在区域、家政公司名称、联系人及联系电话。设定家政性质:免费预约、预约金、实价三种模式可选。通过富文本编辑器编辑服务详情,支持图文排版。 管理家政:对已发布服务进行编辑、上下架等操作。管理订单:查看、处理用户预约订单,跟踪服务进度。服


Codex 工程化实践指南:深入理解 AGENTS.md、SKILL.md 与 MCP
Lei_official2026/3/14

AI 就像自动驾驶,其价值并非让没摸过方向盘的新手上路开车,而在于为熟练的驾驶者节约精力和时间。 在 Codex 的设计中,有三个非常关键的概念: AGENTS.md SKILL.md MCP(Model Context Protocol) 如果把 Codex 看成一个 “AI 工程师”,那么这三个概念相当于: 概念角色AGENTS.md团队开发规范SKILL.md可复用工作流MCP外部系统接口 注意这里的“团队开发规范”不是指人类工程师所组成


端侧RAG实战指南
稀有猿诉2026/3/6

本文译自「On-Device RAG for App Developers: Embeddings, Vector Search, and Beyond」,原文链接medium.com/google-deve…,由Sasha Denisov发布于2026年2月21日。 我们已经探讨了离线 AI 代理的重要性 和如何通过函数调用赋予它们工具。现在,让我们通过赋予它们记忆——即使用 RAG(检索增强生成)搜索和检索你的私有数据——来完善整个图景。 当我开始构建 Flutter Gemma 时,

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读