- 消息队列
- 流处理
- 后端
- 微服务
- 消息路由
【免费下载链接】pulsar
Apache Pulsar - distributed pub-sub messaging system
PIP-156(Build and Run Pulsar Server on Java 17)是 Apache Pulsar 将服务端(Broker)的构建与运行基线从 Java 8/11 提升到 Java 17、并为 ZooKeeper / BookKeeper / Pulsar 全面切换 ZGC 的关键提案。本文以该 PIP 为骨架,结合当前仓库中 README.md、ARCHITECTURE.md、CONTRIBUTING.md 与 conf/pulsar_env.sh 等实现证据,梳理升级动机、版本分层策略、GC 默认值变化、构建配置、测试与发布计划,并对照现状说明这一策略的落地结果,帮助读者理解 Pulsar 的 Java 版本治理模型。
为什么 Pulsar 要升级到 Java 17
PIP-156 提出的时间点是 2022 年 4 月,彼时 Pulsar 官方对构建和运行的最低要求仍是 Java 8 或 Java 11。作者在提案中指出,Java 8/11 相对当时最新发布的 Java 18 已经明显"过时",而近年 Java 版本在两方面带来了实质性收益:
- 垃圾回收(GC):从 JDK 8 到 17,JVM 引入了 ZGC(Z Garbage Collector,JDK 11 引入)与 Shenandoah GC(JDK 12 引入),两者均为低停顿并发收集器,相比 G1GC 能显著降低长暂停(stop-the-world)对延迟敏感型服务的冲击,官方博客与社区分析均验证了 GC 演进带来的收益;
- 语言与 API 特性:例如 Java 15 引入的Text Blocks(文本块),可用于改善 Pulsar Function 的格式化输出与模板代码的可读性。
因此,作为常规维护工作,提案建议把 Pulsar 的构建与运行基线升级到当时最新的 LTS(Long Term Support)版本Java 17。升级的收益目标包括:适配 JDK 新的超高速垃圾收集器、启用 Text Blocks 等新语法、并借助 Java 17 上重新审视的运行时 JVM 参数获得整体性能提升。
需要说明的是,PIP-156 面向的是 Pulsar 2.11 时代;当前仓库主线(master)的基线已进一步演进到 Java 21(详见下文"现状与演进"),但该 PIP 确立的"服务端与服务端工具升级、客户端保持低版本兼容"的分层治理思路至今仍是 Pulsar Java 版本策略的基石。
升级范围的三层设计:Broker 升级、CLI 跟随、Client 不动
PIP-156 最核心的设计是按组件分层决定 Java 目标版本,而非一刀切全部升级:
| 组件 | 变更前 | 变更后(PIP-156 目标) | 理由 |
|---|---|---|---|
| Pulsar Broker(服务端) | 构建与运行需 Java 8 或 11 | 构建与运行均需 Java 17 | 服务端掌控运行环境,可充分享受新 GC 与 JVM 改进 |
| Pulsar CLI 工具 | 随发行版运行在 Java 8/11 上 | 运行版本升级到 Java 17 | CLI 运行在 Admin 环境,且本身依赖 Pulsar 发行版 |
| Pulsar Java Client | 目标版本保持 Java 8 | 保持 Java 8 不变 | 客户端必须兼容用户现有的 Java 运行时环境 |
| client-server 共享模块 | Java 8 | 保持 Java 8 发布 | 与客户端同基线,保证兼容性 |
这一分层策略的关键洞察在于:Broker 是部署者可控的组件,而 Client 是被动运行在用户环境里的组件。把两者绑定在同一个版本上,会让客户端升级变成用户迁移的硬约束;反之,让服务端先吃 Java 17 的红利,客户端继续以 Java 8 为目标发布,就能在不破坏用户既有运行环境的前提下完成服务端现代化。
提案还明确声明本 PIP不涉及任何 API 变更(API Changes: None),即升级是纯内部工程改造,对用户可见的 API 与行为不做任何破坏性调整。
从当前仓库看分层策略的落地形态
当前仓库的 Gradle 约定(build-logic/conventions/src/main/kotlin/pulsar.java-conventions.gradle.kts)完整继承并放大了 PIP-156 的分层思想:
pulsarJavaVersion(默认 21)决定服务端主代码的编译目标,pulsarClientJavaVersion(默认 17)决定客户端/公开 API 的编译目标;- 通过
clientProjects清单把pulsar-client-api、pulsar-client-tools、pulsar-cli-utils、pulsar-functions-api等模块划分到客户端基线; - 编译统一使用
options.release.set(...)(即--release),确保字节码、JDK API 与依赖三方库的签名都严格限制在目标版本内; - 客户端项目额外挂载
verifyClientJavaCompatibility任务(pulsar.java-conventions.gradle.kts 中VerifyJavaCompatibility),在check与assemble阶段校验客户端/API 字节码及其 compile/runtime 依赖,防止不小心引入超出基线版本的 JDK API 或第三方库。
仓库文档对分层结果给出了更细的描述(见 CONTRIBUTING.md 与 ARCHITECTURE.md):Pulsar 5 服务端组件与 Functions 实现要求 Java 21+;Client CLI 工具保持 Java 17 兼容;客户端库、共享依赖与 Functions/IO 公共接口保持 Java 17 兼容;Java 17 编译的 Function 可以运行在 Java 21+ 的 Functions 实例上。也就是说,PIP-156 的"客户端与服务端解耦"原则在当前主线中被进一步细化为"服务端 21 / 客户端与工具 17"的双基线体系。
GC 默认值迁移:从 G1GC 到 ZGC(或 Shenandoah GC)
变更前 vs 变更后
PIP-156 对运行时 JVM 标志的调整是另一项核心工作。提案给出的对比是:
- 变更前:ZooKeeper、BookKeeper、Pulsar 的默认 GC 均为G1GC;
- 变更后:三者的默认 GC 切换为ZGC(或 Shenandoah GC),并在提案中明确:将借助性能测试验证 ZGC / Shenandoah GC 是否确实优于 G1GC,以及新 GC 默认值与相关 Java 标志的合理性。
底层依据是提案的两个假设:其一,期望依赖库(BookKeeper、ZooKeeper)在 Java 17 上正常工作,兼容性问题作为子任务解决;其二,假设 ZGC / Shenandoah GC 对 Pulsar 这类高吞吐、低延迟的消息系统性能优于 G1GC,并用 OpenMessaging 基准测试等验证。
当前仓库中的 GC 默认配置
当前仓库的启动脚本已经把 ZGC 设为默认(同时保留了按 Java 大版本区分行为的能力):
conf/pulsar_env.sh 中:
# Garbage collection options if [ -z "$PULSAR_GC" ]; then PULSAR_GC="-XX:+PerfDisableSharedMem -XX:+AlwaysPreTouch" if [[ $JAVA_MAJOR_VERSION -eq 21 || $JAVA_MAJOR_VERSION -eq 22 ]]; then PULSAR_GC="-XX:+UseZGC -XX:+ZGenerational ${PULSAR_GC}" else PULSAR_GC="-XX:+UseZGC ${PULSAR_GC}" fi ficonf/bkenv.sh 中 BookKeeper(bookie)使用同样的逻辑,且BOOKIE_GC默认回退到${PULSAR_GC}:
BOOKIE_GC="${BOOKIE_GC:-${PULSAR_GC}}" if [ -z "$BOOKIE_GC" ]; then BOOKIE_GC="-XX:+PerfDisableSharedMem -XX:+AlwaysPreTouch" if [[ $JAVA_MAJOR_VERSION -eq 21 || $JAVA_MAJOR_VERSION -eq 22 ]]; then BOOKIE_GC="-XX:+UseZGC -XX:+ZGenerational ${BOOKIE_GC}" else BOOKIE_GC="-XX:+UseZGC ${BOOKIE_GC}" fi fi要点解读:
- 未显式设置
PULSAR_GC/BOOKIE_GC时,默认启用ZGC,并叠加-XX:+PerfDisableSharedMem -XX:+AlwaysPreTouch(禁用共享内存性能监控、启动时预触达所有堆页,减少运行时首触停顿); - 在 Java 21/22 上进一步启用
-XX:+ZGenerational(分代 ZGC),其余大版本走普通 ZGC; bin/pulsar(见 bin/pulsar)在启动 broker 时拼接$PULSAR_MEM $PULSAR_GC $PULSAR_GC_LOG,启动 bookie 时拼接$BOOKIE_MEM $BOOKIE_GC ...;bin/function-localrunner 对本地 Function Runner 应用相同的PULSAR_GC逻辑;bin/pulsar-perf 也会透传调用方设置的PULSAR_GC。若需显式覆盖,可在环境中预置PULSAR_GC/BOOKIE_GC变量,脚本会跳过默认赋值。
GC 日志参数也随 Java 版本分化
脚本还根据 Java 大版本选择不同的 GC 日志语法(这同样是"升级后重访运行时标志"的一部分):
- Java 9+:使用统一日志
-Xlog:gc*,safepoint:${PULSAR_GC_LOG_DIR}/pulsar_gc_%p.log:time,uptime,tags:filecount=10,filesize=20M; - Java 17+:额外前置
-Xlog:async启用异步日志(参考 JDK-8264323,避免 GC 日志 I/O 阻塞应用线程); - Java 8:回退到旧式
-Xloggc:... -XX:+PrintGCDetails -XX:+PrintGCDateStamps ...组合。
这解释了为什么 PIP 要求同时"升级 Java 版本"和"重访运行时标志"——两者必须配套,才能吃到 Java 17 的完整收益。
构建层面的落地:构建要求与兼容性校验
构建所需的 JDK
按 README.md 中记录的版本对应表,PIP-156 对应的 Pulsar 2.11 及之后版本构建需要JDK 17;当前 master 分支则需要 JDK 21(可选 25/26),且 CONTRIBUTING.md 明确 master 构建要求JDK 21、25 或 26(可用-PskipJavaVersionCheck跳过版本检查),同时需要zip命令。构建统一通过项目自带的 Gradle Wrapper(./gradlew)执行,无需单独安装 Gradle。
用 Gradle 属性自定义 Java 目标版本
当前构建系统支持通过属性显式指定目标版本(build-logic/conventions/src/main/kotlin/pulsar.java-conventions.gradle.kts):
-PpulsarJavaVersion=17:将服务端代码与常规测试源码目标设为 Java 17(自定义构建,不参与 CI 默认流程);-PpulsarClientJavaVersion=17:控制客户端/公开 API 目标与消费者测试工具链(17 本来就是默认);-PtestJavaVersion=17:用已安装的 JDK 17 编译并运行测试(仅适用于整个测试依赖图都支持 Java 17 的模块);-PdockerJavaVersion=...:覆盖 Docker 基础镜像中的 JDK 版本(settings.gradle.kts 中有注释说明)。
CONTRIBUTING.md 给出的自定义 Java 17 构建示例:
./gradlew assemble -PpulsarJavaVersion=17 PULSAR_MIN_JAVA_VERSION=17 bin/pulsar standalone其中PULSAR_MIN_JAVA_VERSION用于覆盖启动脚本默认的版本检查(默认 21),它只影响启动时的校验,不改变已编译的字节码或依赖要求;使用 Java 21 专有特性(如虚拟线程)的代码会使 Java 17 目标构建失效。Gradle Docker 构建会把pulsarJavaVersion作为PULSAR_MIN_JAVA_VERSION烘焙进镜像,docker/pulsar/build.gradle.kts 中默认值为 21;直接执行 Docker 构建时也可通过--build-arg PULSAR_MIN_JAVA_VERSION=17覆盖(docker/pulsar/Dockerfile 与 Wolfi 变体默认同为 21)。
测试计划与发布计划
测试策略
PIP-156 规划的验证手段包括:
- 通过 Pulsar OSS CI 测试:主干 CI 全量回归,确保 Java 17 下编译与测试通过;
- 通过 SN 持续集成框架中的集成测试:在更完整的集成环境(含 ZooKeeper、BookKeeper 组合)下验证;
- 新增 Java 8 Client 运行时兼容性集成测试:把"客户端保持 Java 8"从口头承诺变成 CI 强制约束;
- 运行 OpenMessaging 基准测试:对比 Java 17 与旧版本在默认 JVM 标志变化下的性能改进,用于验证 ZGC 默认值调整的收益。
这一"兼容性测试进 CI"的做法延续至今:当前仓库通过:tests:pulsar-client-java-compatibility模块(tests/pulsar-client-java-compatibility/src)在 Java 17 工具链下编译并运行消费者示例(CONTRIBUTING.md 给出命令:./gradlew :tests:pulsar-client-java-compatibility:test -PtestRetryCount=0),从运行时层面验证客户端基线。从代码结构看,该模块还依赖 buildtools 与 testmocks 的 Java 17 兼容版本,用于支撑消费者兼容性测试的加载。
发布计划
PIP-156 将本次升级随Pulsar 2.11 版本发布。结合 README.md 中记录的运行时矩阵,2.10 ≤ ver ≤ 3.0 的 Broker / Functions / IO / CLI 均为 Java 17、Java Client 支持 8/11/17,可见该计划已按预期落地。
现状与演进:PIP-156 之后 Pulsar 的 Java 版本矩阵
PIP-156 确立的"服务端用新版本、客户端保兼容"框架在后续版本中持续演进,当前仓库 README.md 给出了完整的分版本矩阵:
| 版本范围 | Broker | Functions / IO | CLI | Java Client | Docker 运行时 |
|---|---|---|---|---|---|
| master(≥ 4.1) | 21 | 21 | 17 或 21 | 17 或 21 | 21 |
| 3.3 ≤ ver ≤ 4.0 | 21 | 21 | 17 或 21 | 8、11、17 或 21 | 21 |
| 2.10 ≤ ver ≤ 3.0 | 17 | 17 | 17 | 8、11 或 17 | 17 |
| 2.8 ≤ ver ≤ 2.10 | 11 | 11 | 8 或 11 | 8 或 11 | 11 |
| ver < 2.8 | 8 或 11 | 8 或 11 | 8 或 11 | 8 或 11 | 8 或 11 |
同时 README.md 给出两条重要运维建议:
- 选择 Java 版本时应使用带最新 bug 修复与安全补丁的较新小版本(如 17 或 21 系列),例如 JVM 缺陷 JDK-8351933 可能引发 Pulsar 稳定性问题,该缺陷在 Java 17.0.17+ 与 Java 21.0.8+ 中被修复;
- Pulsar 官方 Docker 镜像在发布时内置当时最新的 Java 版本。
小结
PIP-156 是 Pulsar Java 版本现代化的一次里程碑式改造,其核心成果可以归纳为四点:
- 服务端升级:Broker 的构建与运行基线提升到 Java 17,享受新 GC(ZGC / Shenandoah)与 JVM 改进;
- 客户端不动:Java Client 与 client-server 共享模块保持 Java 8 目标,且无任何 API 变更,保证用户运行环境兼容;
- CLI 跟随服务端:CLI 运行版本升级到 Java 17(CLI 运行在 Admin 环境,随发行版分发);
- GC 默认值切换:ZK / BK / Pulsar 默认 GC 从 G1GC 切换为 ZGC(或 Shenandoah GC),并用基准测试验证收益。
这一分层策略在仓库中的实现痕迹清晰可见:Gradle 约定层(pulsar.java-conventions.gradle.kts)与启动脚本层(conf/pulsar_env.sh、conf/bkenv.sh)共同把"服务端新版本、客户端旧兼容、GC 默认 ZGC"固化成了可持续维护的工程约定,并在此后的 Pulsar 3.x / 4.x / 5.x 中演化为更细粒度的双基线(服务端 21 / 客户端 17)体系。对于需要规划消息中间件 JVM 升级的团队,PIP-156 提供的组件分层、GC 切换与兼容性测试三者并行的思路,是一份值得参考的实践范本。
- 消息队列
- 流处理
- 后端
- 微服务
- 消息路由
【免费下载链接】pulsar
Apache Pulsar - distributed pub-sub messaging system
相关推荐
从Java 8到Java 17:《On Java 8》中文版升级指南与实战路径
从Java 8到Java 17:《On Java 8》中文版升级指南与实战路径 为什么需要升级? Java版本迭代速度加快,从Java 8到Java 17已积累
zerobrew 版本演进与能力地图:从 CHANGELOG 看 0.1.1 到 0.3.2 的架构升级之路
zerobrew 版本演进与能力地图:从 CHANGELOG 看 0.1.1 到 0.3.2 的架构升级之路 zerobrew 是一个面向 macOS 与 Li
Apache Pulsar 架构详解:从 Broker、BookKeeper 到 Pulsar Proxy 与服务发现
Apache Pulsar 架构详解:从 Broker、BookKeeper 到 Pulsar Proxy 与服务发现 本文是 Apache Pulsar 官方
消息队列后端流处理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考