一起探讨在云环境中合理调整 Java 应用程序实例集规模的具体挑战和战略。我们将介绍 GraalVM、CRaC 和 Azul Platform Prime(Azul 的高性能 Java 平台)。
在当今以云为中心的 IT 环境中,合理调整计算资源的规模已成为全球开发人员、DevOps 和站点可靠性工程师面临的严峻挑战。合理调整规模的主要驱动力通常是减少资源浪费并降低云支出。但这些考虑因素需要与其他业务需求(例如性能和运营复杂性)进行平衡。目标是找到最佳平衡点:既足够精益以具有成本效益,又足够稳健以确保可靠的服务交付。
虽然大多数云优化战略与编程语言无关,但 Java 应用程序在合理调整规模方面提出了独特的挑战。此外,出现了一类新的 Java 运行时,称为高性能 Java 平台,它们可以缓解 Java 特有的一些问题。这些新一代平台缩短了启动时间,减少了内存占用空间,并具有更可预测的性能特征——从根本上转变了我们在云环境中进行 Java 应用程序规模调整的方式。
一起探讨在云环境中合理调整 Java 应用程序实例集规模的具体挑战和战略。我们将研究关键优化领域,深入探讨每个领域的 Java 特定考虑因素,并演示高性能 Java 平台如何简化这些工作。
合理调整 Java 应用程序规模的目标
在理想情况下,我们只会配置在任何给定时间满足流量所需的资源并为其付费,当不需要这些资源时,会将其关闭。重要的是,这种弹性扩缩不会影响响应时间、服务级别协议 (SLA) 或用户体验。
- V垂直合理调整每个 Pod 的大小,尽可能减少闲置资源。
- 水平合理调整实例集规模,确保有足够的服务器实例来处理负载。
- 设置扩缩策略,根据负载来调整服务器数量。
细节决定成败
既要节省成本,又能提供卓越性能,这对于所有云技术都绝非易事。虽然需求可能会立即激增,但为了满足这一需求,需要花费时间来配置服务器基础设施(K8s 节点、EC2 实例等)。这些冷启动延迟迫使组织做出艰难抉择:要么以更高的成本维持过剩容量以应对潜在的峰值,要么在需求激增期间面临服务降级的风险。大多数公司都倾向于过度配置,优先考虑性能和可靠性,而不是成本效率。
牺牲成本以换取性能的示例包括:
- 无论负载如何,始终让服务器保持开启状态。
- 每台服务器上的资源利用率较低。
对于 Java,成本与性能之间的矛盾更加明显。即使在配置基础设施之后,Java 应用程序也需要额外的时间和 CPU 资源才能全速运行。虽然与 JavaScript 等解释型语言相比,Java 可提供卓越的性能、安全性和可维护性,但您需要在启动时支付一笔费用,有时甚至在稳定状态下也要支付费用。
Java 启动和预热
在弹性环境中启动的 Java 应用程序的生命周期如下所示:
- 节点启动 – 配置虚拟机或容器,包括初始化容器的操作系统。
- JVM 启动 – 加载 Java 虚拟机 (JVM) 的所有内部库,准备运行应用程序。
- 应用程序启动 – 加载代码的所有部分,包括 SpringBoot 初始化等内容,直到应用程序准备好接受第一个事务。
- 应用程序预热 – 根据当前服务器硬件和应用程序使用模式优化 Java 代码以全速运行。此过程称为即时 (JIT) 编译。
高性能 Java 平台
高性能 Java 平台由两个关键组成部分:增强型 JDK 和配套的基础设施服务。增强型 JDK 与长期支持 (LTS) 版本的 Java SE 规范保持完全兼容,同时在三个重要方面对标准 OpenJDK 发行版进行了重大改进:
- 更快的应用程序运行速度
- 更短的启动和预热时间
- 更一致的运行时行为
除了 JDK 之外,高性能 Java 平台还提供与客户端 JVM 配合使用的集中式服务,以实现独立 JDK 发行版无法达到的性能和运营效率水平。
本文涉及的主要技术如下:
- GraalVM(尤其是 GraalVM 原生映像)– GraalVM 是 Oracle 推出的另一个 JDK,基于 Truffle 和 Graal 技术运行。它有一个免费的开源社区版和一个专有的闭源企业版。GraalVM 原生映像是 GraalVM 的一部分,可提前 (AOT) 将 Java 应用程序编译为独立的原生可执行文件,从而消除 JVM 启动开销并减少内存使用量,但代价是丧失一些运行时性能优化和动态 Java 功能。
- 检查点协调恢复 (CRaC) – 由 Azul 领导的 CRaC OpenJDK 项目,旨在通过使 JVM 能够在检查点处捕获和存储完全预热的状态来缩短启动和预热时间。然后,应用程序可以从该检查点恢复,从而绕过通常的初始化和预热阶段,实现近乎即时的性能。CRaC 受多种 JDK(例如 Azul Platform Core、Azul Platform Prime 和 Bellsoft Liberica)以及 AWS Lambda 函数和许多常用应用程序框架(例如 Quarkus 和 SpringBoot)的支持。
- Azul Platform Prime – Azul 的高性能 Java 平台,是 OpenJDK 的优化版本。它包括 Optimizer Hub,这是一组可以帮助服务器相互协作以提升性能的服务。
垂直扩展
垂直扩展是对可用于服务器的 CPU 和 RAM 进行调整的过程,可确保有足够的容量来处理流量峰值,同时避免浪费未使用的容量。传统的虚拟机和物理服务器需要以粗略的增量分配资源,而容器可以极其精准地分配计算资源。
一种常用方法是在 Kubernetes 中使用 Vertical Pod Autoscaler (VPA)。VPA 会监控您的使用情况,然后调整可用于 Pod 的资源,并重新启动 Pod 以使调整生效。
那么,为何不直接对 Java 实例集使用 VPA 并完成相应操作呢?调整 Java 容器大小时,通常需要调整命令行 Java 堆参数以及 Pod 大小,而 VPA 无法这样做。此外,由于 JVM 可以“预留”未使用的内存,因此 VPA 很难正确测量使用情况并进行调整。大多数情况下,VPA 不适用于 Java 应用程序,您需要手动设置 Java 容器的资源限制。
Java 应用程序的问题在于,运行之初,当 JVM 预热应用程序时,JIT CPU 的活跃度很高。

通常,您必须为编译峰值预留 CPU 容量,即使该容量在稳定状态期间闲置也是如此。换句话说,您要为仅在应用程序运行开始时持续几分钟的峰值永久付费。
Typically, you have to reserve CPU capacity for that compilation spike even though that capacity will sit idle during steady state. In other words, you’re paying forever for a spike that only lasts a few minutes at the beginning of your application’s run.
高性能 Java 平台有何帮助
用于减少因 JIT CPU 峰值造成的容量浪费的高性能 Java 平台包括:
- GraalVM 原生映像中的 AOT
- 优点 – 通过在应用程序运行之前执行所有优化,AOT 减少了运行应用程序所需的 CPU 和内存量。
- 缺点 – GraalVM 原生映像不适用于大部分现有 Java 代码,因为 AOT 无法处理许多 Java 模式。AOT 代码的执行速度比 JIT 所生成代码的执行速度慢。
- CRaC
- 优点 – 通过在 JIT 衰减后对应用程序设置检查点,可以在小型计算机上的 CPU 峰值后进行恢复。
- 缺点 – 采用 CRaC 的现有应用程序需要更改代码,才能正确恢复应用程序。由于 CRaC 尚未被广泛采用,因此极少常用库进行了此类更改。拍摄快照之前,还需要采取措施来清理用于预热计算机的事务的状态。最后,您仍需要为应用程序生命周期后期可能发生的额外 JIT 活动(去优化)预留容量。
- Azul Platform Prime – Optimizer Hub
- 优点 – 通过将 JIT 转移到外部云原生编译器服务,Azul Optimizer Hub 可处理任何代码。由于 Optimizer Hub 可处理去优化风暴以及初始预热,因此您可以放心地删除为 JIT 预留的所有 CPU。
- 缺点 – Azul Platform Prime 是基于 OpenJDK 的商业解决方案,并且 Optimizer Hub 的配置和维护更加复杂。
“世事难料”
人们预留大量闲置容量(特别是对于延迟敏感型应用程序)的另一个原因是“世事难料”。从垃圾收集暂停到去优化风暴,再到 JVM 在执行长时间运行的任务时锁定部分资源,您必须处理 JVM 上各种导致峰值的行为。因此,人们经常为其容器配置低至 35% 的 CPU 利用率阈值,以便为这些峰值预留容量。
高性能 Java 平台有何帮助
- Azul Platform Prime – C4 无暂停垃圾收集器允许应用程序在执行 GC 时继续接受请求,从而消除了大多数 GC 暂停。Prime 的 ReadyNow 技术可防止因去优化风暴(应用程序使用模式的转变迫使 JVM 丢弃并重新编译优化代码的事件)引起的性能中断。通过在应用程序重新启动时维护和智能地重用优化配置文件,ReadyNow 即使在工作负载模式更改时也能确保一致的性能。
- GraalVM – GraalVM 原生映像会提前优化所有代码,这意味着当使用模式更改时不会出现去优化风暴。AOT 编译的副作用是代码运行速度比使用 JIT 编译优化的代码运行速度慢。

水平实例集规模调整
水平实例集规模调整是设置任何时候运行的服务器数量以满足当前流量需求的过程。所需服务器的数量是每台服务器承载能力(即,每台服务器在满足 SLA 的情况下可以处理多少事务)的函数。
缩减水平实例集规模的妥善方法是让每台服务器完成更多的工作。一些高性能 Java 平台具有先进的 JIT 编译器,可以使用比 OpenJDK 更低的 CPU 执行各个事务,因此可在不触发基于 CPU 的自动扩缩策略的情况下完成更多事务。
高性能 Java 平台解决方案
- Azul Platform Prime – Falcon JIT 编译器所生成代码的运行速度比标准 OpenJDK 最多快 40%。Azul Platform Prime 还消除了大多数的应用程序暂停和抖动情况,使得采用基于延迟的 SLA 的服务器具有更强的承载能力。
- GraalVM – 虽然 GraalVM CE 和 GraalVM 原生映像生成代码的速度均比 OpenJDK 慢,但付费版 GraalVM EE 拥有先进的 JIT 编译器,可以比 OpenJDK 更快地生成代码。paid GraalVM EE has an advanced JIT compiler that produces code faster than OpenJDK.
扩缩服务器以满足需求
优化服务器以节省成本的绝佳方法就是完全关闭服务器。云的弹性特性意味着您可以按计划扩缩服务器,也可以根据负载自动扩缩,以便只为使用的内容付费。
不过,自动扩缩虽然听起来简单,实际上却很复杂,通常需要重新设计架构。即使是为扩缩而编写的应用程序,也会遇到 Java 启动和预热问题,因此在操作上很难确保新配置的服务器具有出色性能。开发人员和 DevOps 投入大量时间弄清楚如何让这些服务器做好准备,尽早以足够的速度接受流量,以应对突然的流量峰值。
总而言之,上述高性能 Java 解决方案的优缺点如下:
- GraalVM 原生映像
- 优点 – 解决启动和预热问题,完成第一个事务的时间通常以毫秒计,并且运行时没有 JIT。
- 缺点 – 不适用于较大比例的现有 Java 代码,因为 AOT 无法处理许多 Java 模式。AOT 代码的执行速度比 JIT 所生成代码的执行速度慢。
- CRaC
- 优点 – 解决了启动和预热问题,但即使流量模式发生更改,也能实现全代码速度。
- 缺点 – 采用 CRaC 的现有应用程序需要更改代码,才能正确恢复应用程序。由于 CRaC 尚未被广泛采用,因此极少常用库进行了此类更改。拍摄快照之前,还需要采取措施来清理用于预热计算机的事务的状态。最后,您仍需要为应用程序生命周期后期可能发生的额外 JIT 活动(去优化)预留容量。
- Azul Platform Prime 和 Optimizer Hub
- 优点 – 通过将 JIT 转移到外部云原生编译器服务并从其他服务器学习出色的优化模式,Optimizer Hub 使您的应用程序比 OpenJDK 更快地全速运行。由于 Optimizer Hub 可处理去优化风暴以及初始预热,因此您可以放心地删除为 JIT 预留的所有 CPU。
- 缺点 – Azul Platform Prime 是基于 OpenJDK 的商业解决方案,并且 Optimizer Hub 的配置和维护更加复杂。
Conclusion
如果在 Java 上经营业务,您在尝试平衡成本与性能和运营灵活性时会遇到一些特别的问题。使用高性能 Java 平台就无需进行一些权衡,并在保证相同或更优性能的情况下降低云成本。
借助高性能 Java 平台,您可以:
- 消除每台服务器上浪费的资源(垂直合理调整规模)。
- 以尽可能少的服务器满足需求(水平合理调整规模)。
- 根据当前负载动态扩缩服务器(自动扩缩)。
This article originally appeared in The New Stack.