Hazelcast Jet 代码部署增强:`addClass` 嵌套类与 `addPackage` 包级部署机制全解析
缓存KV存储消息队列流处理后端【免费下载链接】hazelcastHazelcast is a unified real-time data platform combining stream processing with a fast data store, allowing customers to act instantly on>项目地址https://gitcode.com/gh_mirrors/ha/hazelcast点击查看免费下载本文围绕 Hazelcast Jet现已并入 Hazelcast 统一实时数据平台自 4.1 版本引入的代码部署增强Code Deployment Improvements展开作业提交时如何把内部类、匿名类乃至整个包递归地注入到 Jet 作业的 classpath 中。文章将以设计文档 docs/design/jet/001-code-deployment-improvements.md 为骨架并结合仓库源码与测试深入讲解JobConfig.addClass()/addPackage()的底层实现、classgraph 库的着色使用方式、资源在集群中的存储与内存开销以及可验证的测试行为。读完本文你将能够正确地为 Jet 作业配置类与包级资源理解“资源以 IMap 存储、主副本 备份副本双份驻留内存”的容量规划要点并能针对常见坑点如 light job 限制、非现有包名行为写出可运行的部署代码。背景为什么需要“嵌套类”与“包级”部署在 Hazelcast Jet 中一个作业Job由 Pipeline 或 DAG 描述作业的代码类、JAR、classpath 资源通过JobConfig提交到集群由 Jet 的专用 classloader 在成员节点上加载执行。早期JobConfig只支持添加单个类、单个 JAR 或单个 classpath 资源用户面临两个典型的部署痛点嵌套类无法整体携带一个业务类往往附带内部类inner class与匿名类anonymous class例如 Kotlin lambda、Runnable匿名实现、EntryProcessor 中常见的匿名内部类。手工逐个addClass()不现实且容易遗漏。包级资源无法批量携带用户希望一次把某个包package下的所有类与资源一次性注入作业 classpath而 Java 标准 API 并不支持“列出某个包下的所有类”。设计文档把目标概括为User should be able to add nested (inner anonymous) classes as well as entire packages to Jet jobs classpath.问题陈述Java 反射与 ClassLoader 的能力边界为什么这个需求在技术上并不平凡设计文档明确指出两个硬性约束Java 反射无法列出匿名类给定一个类标准反射 API 只能通过getDeclaredClasses()拿到声明在源码中的成员类却无法获知该类在方法体内创建的匿名类如new Runnable() {...}、Kotlin lambda 生成的Foo$1之类的类。这些匿名类同样需要被 JVM 加载才能反序列化执行。ClassLoader 不支持列出包内容ClassLoader.getResources()只能按确定的资源名查找无法回答“这个包下有哪些类文件”这类目录列举问题。换句话说仅靠Class.forName 反射无法穷举一个类“实际依赖的所有字节码文件”。解决方案借助 classgraph 扫描包资源设计文档给出的解决思路非常直接既然嵌套类与根类位于同一个包package下那么可以列出该包关联的所有资源 URL再提取并过滤出想要的 class 文件——这一策略同时要处理“列目录、从任意嵌套 JAR 中解压、处理自定义 URL scheme”等场景同样的策略也可递归用于找出一个包内的全部类。与其从零实现一套 classpath 扫描器Jet 选择引入一个轻量级开源库 ——classgraph文档记载体积约 470KBJet 通过 Maven Shade 将其着色打包进自己的发行物中避免与用户 classpath 冲突。classgraph 支持广泛的 classpath 规格机制可以扫描目录、嵌套 JAR、file:/jar:等常见 URL scheme提供包级package与路径级path的接受规则返回类信息ClassInfo及其内部类关系getInnerClasses()返回非 class 资源如包内的package.properties、静态资源文件。在仓库源码中可以看到 Jet 对 classgraph 的实际使用见 ReflectionUtils.javaio.github.classgraph.ClassGraph、ClassInfo、ScanResult的导入与调用。API 变更JobConfig的两个新能力设计文档为JobConfig提出两项 API 扩展两者在 JobConfig.java 中均有完整实现。1. 增强addClass()递归添加嵌套类原文档给出的签名since 4.1Nonnull SuppressWarnings(rawtypes) public JobConfig addClass(Nonnull Class... classes)仓库中的实际实现JobConfig.javaNonnull public JobConfig addClass(Nonnull Class?... classes) { throwIfLocked(); checkNotNull(classes, Classes cannot be null); ResourceConfig.fromClass(classes).forEach(cfg - resourceConfigs.put(cfg.getId(), cfg)); return this; }其语义Javadoc 原文要点递归添加给定类及其所有嵌套内部与匿名类到 Jet 作业 classpath这些类只对挂接在该 Pipeline / DAG 上的代码可见对其他代码不可见——重要示例是IMap数据源它只能实例化 Jet 实例 classpath 中的类即集群侧代码不能用于 light jobJetService#newLightJob(Pipeline)底层存储是默认备份数为 1 的IMap因此添加大文件时需按内存扩容集群每个文件在集群内会有2 份拷贝主副本 备份副本。注意类被加入后返回this以支持 Fluent API 链式调用addClass与addPackage调用前都会先throwIfLocked()——JobConfig一旦被提交locked便不可再修改。2. 新增addPackage()递归添加整个包原文档给出的签名Nonnull public JobConfig addPackage(Nonnull String... packages)仓库中的实际实现JobConfig.javaNonnull public JobConfig addPackage(Nonnull String... packages) { checkNotNull(packages, Packages cannot be null); Resources resources ReflectionUtils.resourcesOf(packages); resources.classes().forEach(classResource - add(classResource.getUrl(), classResource.getId(), CLASS)); resources.nonClasses().forEach(this::addClasspathResource); return this; }语义要点递归添加给定包内的全部类与资源非 class 文件如package.properties到 Jet 作业 classpath包内每个 class 文件以ResourceType.CLASS注册资源 ID 即“包路径/类名.class”非类资源则以addClasspathResource方式注册同样不能用于 light job同样受 IMap 双副本内存开销约束与addClass不同addPackage对传入的包名数组做了checkNotNull但不调用throwIfLocked()——从源码结构看包内资源是在扫描完成后逐条通过私有add(...)注册的。配套 API 一览设计文档提到可参考的addJar与addClasspathResource仓库中 JobConfig.java 提供了一系列重载方法入参形式资源 ID 规则说明addJar(URL)URLURL 最后一段路径filename若 ID 已存在则不替换addJar(File)File文件名同上要求是文件ensureIsFileaddJar(String)路径字符串文件名内部转File再转 URLaddJarsInZip(URL/File/String)ZIP 内 JAR按 JAR 文件名一次添加 ZIP 包内多个 JARaddClasspathResource(URL/File/String)单资源URL/文件名另有带显式id的重载addClass(Class...)类对象包路径/类名.class递归嵌套类addPackage(String...)包名数组各 class/资源的路径递归包内类与资源所有资源的实际存储形态为ResourceConfigResourceConfig.java它是一个实现IdentifiedDataSerializable的配置对象携带URL、可空id与ResourceTypeCLASS/JAR/CLASSPATH_RESOURCE。源码级原理ReflectionUtils如何用 classgraph 干活addClass与addPackage的核心逻辑都汇聚在工具类ReflectionUtilsReflectionUtils.java。nestedClassesOf()找出一个类及其全部嵌套类ResourceConfig.fromClass()的实现ResourceConfig.javapublic static StreamResourceConfig fromClass(Nonnull Class?... classes) { return ReflectionUtils.nestedClassesOf(classes).stream().map(ResourceConfig::new); }nestedClassesOf()ReflectionUtils.java的关键步骤构造ClassGraph并开启.enableClassInfo()与.ignoreClassVisibility()——后者保证即使嵌套类是私有的也能被发现收集传入类的classloader去重后逐个addClassLoader并把每个类所在的包名作为acceptPackages规则传入scan()后先按类名过滤出目标ClassInfo再对其调用getInnerClasses()取出所有内部/匿名类信息最后loadClass()得到真实的Class?对象返回传入类本身 全部嵌套类的合并集合。这一步正是设计文档中“先列出包资源、再过滤目标类文件”策略的实现由于嵌套类与根类同包acceptPackages加上getInnerClasses()的关系遍历即可补全反射无法枚举的匿名类。resourcesOf()递归扫描包内的类与资源addPackage()调用的resourcesOf(String... packages)ReflectionUtils.java把包名com.example.foo转换成路径com/example/foo.replace(., /)ClassGraph同时acceptPackages(packages)与acceptPaths(paths)ignoreClassVisibility()scan()后getAllClasses()得到包内全部类包装成ClassResource集合getAllResources().nonClassFilesOnly()得到包内非 class 资源 URL 集合打包返回Resources含 classes 与 nonClasses 两部分。回到JobConfig.addPackage()classes 以CLASS类型逐条add(...)nonClasses 交给addClasspathResource从而实现对“包内一切内容”的完整携带。测试验证从单测看行为契约设计文档特别提到“In particular created unit test proving Kotlin lambdas (as anonymous classes) are added to the classpath”即专门有单测证明 Kotlin lambda本质是匿名类能被加入 classpath。仓库中的ResourceConfigTestResourceConfigTest.java用四类用例锁定了行为when_addClassWithClass第 62-71 行addClass(ResourceConfigTest.class)后资源 ID 等于ReflectionUtils.toClassResourceId(类)即com/hazelcast/jet/config/ResourceConfigTest.class资源类型为ResourceType.CLASSwhen_addResourcesWithPackage第 73-92 行addPackage(本类所在包名)后资源配置集合中既包含本类的 CLASS 资源也包含package.properties这类 CLASSPATH_RESOURCE——验证“包内类 资源一并携带”when_addResourcesWithNonExistingPackage第 94-103 行对不存在的包名thispackage.does.not.exist调用addPackage返回空集合而不抛异常空操作语义JAR 相关用例第 105-159 行addJar(URL)以 URL 末段为资源 IDURL 无路径段时抛IllegalArgumentException相同资源 ID 重复添加抛异常不静默覆盖。此外JobConfigTest.java 覆盖了addClass/addPackage在JobConfig层面的注册行为JetTest.java 展示了new JobConfig().addClass(JetTest.class)的典型用法部署相关测试如 AbstractDeploymentTest.java、ClientDeployment_StandaloneClusterTest.java则验证了带用户代码部署配置的客户端如何通过addClass把类随作业提交到集群。典型用法示例把上述 API 组合起来一个典型的 Jet 作业配置如下Fluent API 风格JobConfig jobConfig new JobConfig() // 递归携带该类的所有内部类与匿名类含 Kotlin lambda 生成的类 .addClass(MyEntryProcessor.class, MyFunction.class) // 递归携带整个包内的类与资源 .addPackage(com.example.jet.functions, com.example.jet.mappers) // 携带独立 JAR 与任意 classpath 资源 .addJar(/path/to/lib-extra.jar) .addClasspathResource(/path/to/config.properties); Pipeline p Pipeline.create(); p.readFrom(Sources.list(input)) .map(new MyFunction()) // 引用 addClass 加入的类 .writeTo(Sinks.list(output)); JetInstance jet Jet.newJetClient(); jet.newJob(p, jobConfig).join();使用须知来自 Javadoc 与源码行为作用域这些资源只对附着于本作业 Pipeline / DAG 的代码可见IMap数据源等集群侧组件无法使用它们light job 限制addClass/addPackage/addJar/addClasspathResource均不可用于 light job内存规划资源以IMap存储、默认备份数为 1每个资源在集群中驻留2 份主副本 备份副本提交大文件前需按此评估成员内存ID 唯一性JAR 类资源以文件名URL 末段为 ID重复 ID 会抛IllegalArgumentExceptionaddClass/addPackage生成的资源 ID 为包路径形式重复添加同一类/包会覆盖同 ID 条目源码中resourceConfigs.put(cfg.getId(), cfg)空包行为addPackage对不存在的包名表现为空操作不抛异常见测试用例。局限与演进方向设计文档在末尾列出了一项“未来改进”ImprovementsMaybe instead of forcing user to manually add (addClass(),addPackage(),addJar()…) all the required resources to the classpath we could scan the classpath and add them automatically - however we would need to figure out the way to filter out unneeded ones so we don’t end up with bloated job’sIMapstate.即理想方案是自动扫描 classpath 并推断作业所需资源免去用户手工addClass/addPackage/addJar的负担难点在于如何过滤无关类避免作业的IMap状态被无用资源撑爆——这与本文反复强调的内存双副本约束直接相关也是自动部署方案必须解决的核心权衡。从当前仓库源码结构看addClass的递归嵌套类能力与addPackage的包级扫描能力已经完整落地自动扫描仍属于文档层面的设计展望而非已实现功能。总结能力自 4.1 起JobConfig.addClass()递归携带嵌套类含匿名类与 Kotlin lambda新增的addPackage()递归携带包内全部类与资源原理Java 反射无法枚举匿名类、ClassLoader 无法列出包内容Jet 借助轻量级开源库 classgraph 扫描包级资源并过滤目标类相关逻辑集中在 ReflectionUtils.java实现落点JobConfig.java 的addClass/addPackage、ResourceConfig.java 的fromClass行为契约由 ResourceConfigTest.java 等测试锁定类/包/资源 ID 规则、空包空操作、JAR ID 冲突异常运维要点资源经IMap双副本存储需按内存扩容light job 不可用作用域仅限作业自身 Pipeline / DAG。在设计文档 001-code-deployment-improvements.md 之外读者可继续深入 JobConfig.java、ReflectionUtils.java 与 ResourceConfigTest.java 获取一手实现与验证证据。赞分享缓存KV存储消息队列流处理后端【免费下载链接】hazelcastHazelcast is a unified real-time data platform combining stream processing with a fast data store, allowing customers to act instantly on>项目地址https://gitcode.com/gh_mirrors/ha/hazelcast点击查看免费下载相关推荐使用 Helm Chart 在 Kubernetes 上部署 Hazelcast Jet 流处理集群的完整指南使用 Helm Chart 在 Kubernetes 上部署 Hazelcast Jet 流处理集群的完整指南 Hazelcast Jet 是一个构建在 Haz不只是 Loadingeasy-loading-cj Toast 功能实战图标、偏移、尺寸一次讲透不只是 Loadingeasy loading cj Toast 功能实战图标、偏移、尺寸一次讲透 easy loading cj 是一款面向 Cangji缓存KV存储消息队列流处理后端DeepMosaics 预训练模型完全指南模型分类、下载部署与源码级调用机制解析DeepMosaics 预训练模型完全指南模型分类、下载部署与源码级调用机制解析 本篇指南以 DeepMosaics 仓库官方文档 docs/pre trai人工智能深度学习计算机视觉图像处理视频处理上一篇Flet Map 地图控件完整指南在 Python 应用中集成可交互的矢量地图下一篇GSD-2 项目迁移指南从 v1 .planning 平滑迁移到 DB 驱动的 .gsd 格式创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

TimePro:双感知hyper-state的Mamba多变量长期预测模型

TimePro:双感知hyper-state的Mamba多变量长期预测模型

先聊点题外话。做长时间序列预测的朋友,应该都体会过那种“模型在验证集上还行,一往后预测几十步就开始摆烂”的挫败感。传统Transformer把注意力铺满整个序列,算力耗尽还抓不住跨变量的滞后影响;LSTM虽然有记忆,但在超…

2026/10/7 16:16:05 阅读更多 →
Metro UI CSS Image Grid 组件实战:响应式图片网格的自动横竖分类与配置详解

Metro UI CSS Image Grid 组件实战:响应式图片网格的自动横竖分类与配置详解

前端UI组件 【免费下载链接】Metro-UI-CSS A progressive front-end framework for creating high-performance responsive reactive web applications! 项目地址: https://gitcode.com/gh_mirrors/me/Metro-UI-CSS 点击查看 免费下载 Image Grid 是 Metro UI CSS …

2026/10/8 17:57:53 阅读更多 →
tiny-gpu Verilog 重构实战:3 个维度让教学级硬件代码质量翻倍

tiny-gpu Verilog 重构实战:3 个维度让教学级硬件代码质量翻倍

tiny-gpu Verilog 重构实战:3 个维度让教学级硬件代码质量翻倍 【免费下载链接】tiny-gpu A minimal GPU design in Verilog to learn how GPUs work from the ground up 项目地址: https://gitcode.com/GitHub_Trending/ti/tiny-gpu 打开 src/core.sv&#…

2026/10/8 17:57:51 阅读更多 →

最新新闻

打造个性化 CLI 命令检索器:用模糊语义匹配替代繁琐的 man 手册翻阅

打造个性化 CLI 命令检索器:用模糊语义匹配替代繁琐的 man 手册翻阅

长期在终端下作业的工程师,几乎都有过被庞杂的命令行参数折磨的经历。像 tar -czvf 或 ps aux 这种形成肌肉记忆的命令固然不在话下,但面对一些低频却关键的诊断场景时,人类大脑的记忆带宽就显得捉襟见肘。例如:如何在多网卡环境下…

2026/10/8 19:02:46 阅读更多 →
智慧社区养老健康管理系统:Spring Boot+Vue+MySQL实战与避坑

智慧社区养老健康管理系统:Spring Boot+Vue+MySQL实战与避坑

简介:一份基于SpringBootVue的智慧社区居家养老健康管理系统源码,适合计算机、电子信息类专业学生用作毕业设计、课程设计或期末大作业。系统采用B/S架构和MVC分层,围绕MySQL、MyBatis、Ajax、ElementUI等技术栈实现,覆盖用户信息…

2026/10/8 19:02:46 阅读更多 →
Superpowers技能包:让Claude Code按工程方法论自动推进任务

Superpowers技能包:让Claude Code按工程方法论自动推进任务

你有没有遇到过这种情况:装了 Cluade Code,却总觉得它只是一个听话的聊天机器人——让它写个函数、改个 bug 没问题,但遇到"帮我把整个项目梳理一遍然后出一个重构方案"这种任务时,它就变得有点平庸,给出的结…

2026/10/8 19:02:46 阅读更多 →
OpenClaw 对接钉钉实战:从单向 Webhook 到双向 Stream 机器人

OpenClaw 对接钉钉实战:从单向 Webhook 到双向 Stream 机器人

把 AI 接进钉钉这件事,我前后折腾了三个周末,最后发现真正的坑基本都集中在钉钉开放平台那一层:权限配错、加签算法算错、机器人发了消息群里却看不到。OpenClaw 本身反而是最省心的部分。如果你也想在钉钉群里养一个“AI 同事”,…

2026/10/8 19:02:46 阅读更多 →
sudo make提权实战:从CTF漏洞到Linux服务器权限加固

sudo make提权实战:从CTF漏洞到Linux服务器权限加固

CTF圈子里有一句老话:拿到 shell 不算本事,能把 sudo 那条规则玩出花才算。DEF CON CTF 的这道 “Sudo Make Me a Sandwich”,外行看以为是段子,内行一看标题就明白——出题人把经典的 “sudo make me a sandwich” 梗直接做成了权…

2026/10/8 19:02:46 阅读更多 →
富士施乐S2520扫描到电脑设置:SMB/FTP共享连接与故障排查

富士施乐S2520扫描到电脑设置:SMB/FTP共享连接与故障排查

简介:针对富士施乐S2520打印机的扫描连接设置指南,面向企业办公人员、IT运维及初次配置网络扫描的用户,解决扫描文件如何保存到电脑指定文件夹、打印机与电脑网络通信两大常见问题。PDF文档总共1个文件,体积约1.05MB,为…

2026/10/8 19:01:44 阅读更多 →

日新闻

抖音用户主页视频数据爬虫详解:点赞、收藏、分享字段抓取与 TaoToken 统一 Key 配置

抖音用户主页视频数据爬虫详解:点赞、收藏、分享字段抓取与 TaoToken 统一 Key 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 0:00:03 阅读更多 →
AI 编程 Trae 国内版与国际版一篇讲透:TaoToken 统一 Key 接入实测

AI 编程 Trae 国内版与国际版一篇讲透:TaoToken 统一 Key 接入实测

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 0:00:06 阅读更多 →
Claude Desktop 配置第三方推理接口教程:用 TaoToken 统一 Key 打通 API 调用

Claude Desktop 配置第三方推理接口教程:用 TaoToken 统一 Key 打通 API 调用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 0:00:07 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 14:34:12 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 13:34:55 阅读更多 →