当前位置: 首页>后端>正文

Spring Boot 3.2 和 Spring Framework 6.1 添加了 Java 21、虚拟线程和 CRaC

Spring Boot 3.2 和 Spring Framework 6.1 添加了 Java 21、虚拟线程和 CRaC

关键要点

  • Spring Framework 6.1 和 Spring Boot 3.2 支持 Java 21,这是 Java 的最新 LTS 版本,而 Java 17 仍然是基准。
  • 虚拟线程的首次亮相简化了并发编程并使其更加高效,同时在响应式编程和 Kotlin 协程方面也进行了改进。
  • 检查点协调还原 (CRaC) 是一种在 JIT JVM 中扩展到零启动时间的新方法,而使用现有的 GraalVM Native Image 支持扩展到零可以显著提升性能。
  • Spring Framework 6.2 和 Spring Boot 3.4 计划于 2024 年 11 月发布,将支持 Jakarta EE 11,并将与 Project Leyden 的预留优化保持一致,以加快启动速度。
  • Spring Boot 是一个流行的新 Java 应用程序框架,将于 2024 年 4 月迎来 10 岁生日,而其基础 Spring Framework 将在一个月前迎来 20 岁生日。

Spring Framework 6.1(2023 年 11 月 16 日发布)和 Spring Boot 3.2(2023 年 11 月 23 日发布)在 Java 21 上运行。这些版本将使虚拟线程的并发编程更简单、更高效,并改进反应式编程和 Kotlin 协程。为了减少“缩减到零”的启动时间,最初支持 OpenJDK 项目 Checkpoint 的协调还原 (CRaC),而现有的 GraalVM 本机映像支持通过新的 GraalVM 版本获得了相当大的性能提升。Spring Framework 6.2 和 Spring Boot 3.4 计划于 2024 年 11 月发布,将支持即将发布的 Jakarta EE 11,并将与 Project Leyden 的预留物优化保持一致。

Spring 的新所有者 Broadcom 在 2023 年 11 月 22 日 Spring Boot 3.2 发布的前一天完成了对前所有者 VMware 的收购。

Spring Framework 将于 2024 年 3 月满 20 岁,而 Spring Boot 将在一个月后满 10 岁。

新的 LTS 版本 Java 21,但 Java 17 仍然是基准

Java 21 是 2023 年 9 月发布的长期支持 (LTS) 版本,现在与 Java 17-20 一样,在运行时是一等公民。Java 21 最终确定了虚拟线程(请参阅下一节),改进了 Z 垃圾回收器,在记录类型检查中提供了用于更紧凑代码的记录模式,并使用模式匹配简化了一些 switch 语句。Oracle Java 布道者 Nicolai Parlog 在他的视频“从 Java 17 升级到 21”中详细介绍了从 Java 17 到 Java 21 的变化。

但 Java 17 是 2021 年 9 月发布的 LTS 版本,仍然是 Spring Boot 3 和 Spring Framework 6 的基准 JDK。2022 年 11 月,这些版本还增加了对 Jakarta EE 9 和 GraalVM Native Image 的支持,并通过 Micrometer 的跟踪和指标增加了嵌入式可观测性。

预计 Spring Framework 6.x 和 Spring Boot 3.x 也将支持 Java 的下一个 LTS 版本 Java 25,该版本可能会在 2025 年 9 月到来。

提高效率的新方法:虚拟线程

虚拟线程简化了 Java 21 中的并发编程,是一种提高效率的新方法。应用程序无需配置线程池或使用回调,只需获取虚拟线程并使用它即可。Java 将每个虚拟线程挂载到一个平台线程,称为载体线程。当一个虚拟线程阻塞输入/输出 (I/O) 时,如数据库请求或 HTTP 调用,Java 会将载体线程用于另一个虚拟线程。

这可以显著提高命令性、I/O 密集型 Java 应用程序的可伸缩性,因为它们可以处理更多的并发请求。但是,CPU 密集型应用程序将看到较少的好处,因为卸载载体线程的机会较少。这同样适用于内存密集型应用程序,因为 JVM 可用的内存将限制并发虚拟线程的数量。

正如 Oracle 的 Java 语言架构师 Brian Goetz 所指出的那样,虚拟线程简化了编程,因为它们“为我们提供了许多我们每天认为理所当然的东西:顺序控制流、局部变量、异常处理、单步调试和分析”。

当虚拟线程命中 I/O 操作或在 Java 代码节中使用锁时,它们会失去效率优势;托架螺纹被阻塞,即所谓的螺纹固定。多个 JDBC 驱动程序演示了此行为。Spring 生态系统中的库也可以固定虚拟线程,一些 Java 库也是如此。synchronized

Spring Boot 3.2 用户通过在 Java 21 上运行并将属性设置为 来启用虚拟线程。然后,Tomcat 和 Jetty 将使用虚拟线程,以及 和 beans、Kafka 和 RabbitMQ 的侦听器、新的 HTTP 以及 Spring 生态系统的许多其他部分。spring.threads.virtual.enabledtrueapplicationTaskExecutortaskSchedulerRestClient

虽然虚拟线程处理单个任务,但 Java 中的结构化并发简化了这些任务的协调。Java 21 中的这个预览功能需要更多时间来烘烤;它可能会采取不寻常的步骤,再次发布其 Java 21 预览版,在 Java 22 中保持不变,以获得更多反馈。

提高效率的既定方法:响应式编程和 Kotlin 协程

响应式编程是扩展 Java 的另一种方式。Spring Framework 自 2017 年 9 月的第 5 版以来一直支持它。反应式编程模型比使用虚拟线程更复杂,因为管理回调和调试更具挑战性。这可能就是为什么 Goetz 在 2021 年 8 月预测“Loom 将扼杀反应式编程”。

这是一个有争议的评论,因为 Project Loom “没有解决响应式编程支持的许多其他功能,即背压、变化传播、可组合性。[…]毕竟,这只是一种不同的线程创建方式,“monday.com 高级工程技术主管 Tomasz Nurkiewicz 指出。

Broadcom似乎同意Nurkiewicz的观点,因为它改进了对反应式编程的支持。例如,它增加了对缓存和调度的反应式支持。

Spring Framework 还改进了对 Kotlin 协程的支持,自 2018 年以来,它简化了异步任务的协调。协程与即将推出的 Java 结构化并发(Java 22 中的预览功能)具有相同的目标。Kotlin 协程可以在 Spring Framework 6.1 中使用面向方面的编程 (AOP)。他们还可以使用虚拟线程来提高效率。

扩展到零的新方法:CRaC

Broadcom 在“运行时效率”主题下支持 OpenJDK 项目 CRaC。更便宜的托管、可持续的计算以及使 JVM 成为优秀的 Kubernetes 公民是该计划的好处。CRaC 是 Spring 应用程序以亚秒级启动时间“扩展到零”的一种新方法。

“缩放到零”表示除非有传入请求,否则不会运行任何应用程序实例。这节省了处理成本,但需要近乎即时的应用程序启动。传统上,Java 的启动速度太慢,无法实现这种“规模化为零”。

Spring Framework 6.1 和 Spring Boot 3.2 提供了对 CRaC 的初始支持,这大大缩短了 Java 应用程序的启动时间及其达到峰值性能的时间。用户在正在运行的 Java 应用程序中触发检查点。然后,CRaC 将应用程序的快照写入磁盘。该快照可以在应用程序的后续运行中还原。

2023 年 10 月 4 日,Broadcom 高级软件工程师兼 Spring Framework Core Committer 的 Sébastien Deleuze 在比利时 Devoxx 大会上与 CRaC 分享了 Spring PetClinic 应用程序的结果。在 Azure 1 CPU 2 GB RAM 云服务器上,启动时间从 11.97 秒缩短到 210 毫秒,速度提高了 56 倍。在 Azure 2 CPU 4 GB RAM 云服务器上,5.76 秒变成了 130 毫秒,快了 44 倍。

Broadcom 通过将检查点的获取及其恢复到现有的 Spring Bean 生命周期阶段来添加 CRaC 支持;检查点映射到 Spring Application Context 的完全停止,而还原映射到其重新启动。Broadcom 借此机会重新审视了 Spring Bean 生命周期模型,该模型在 Spring 3.0 中于 2009 年推出。自动实现生命周期的组件参与 CRaC 检查点和还原。Broadcom 还将现有的 Spring 组件转移到生命周期中,例如任务调度程序、连接池和其他资源持有组件。Spring 将支持 Spring Application Context 的多次连续停止和重启。

这些 Spring 更改是必需的,因为 CRaC 要求所有文件、套接字和池在检查点关闭,并在快照还原后重新打开。应用程序及其所有库必须支持此功能,否则检查点将失败。

除了关闭和重新打开资源之外,CRaC 还有更多的权衡:首先,它只适用于 Linux,因为它依赖于 Linux 功能用户空间中的检查点还原 (CRIU)。这在生产环境中是一个很小的权衡,因为大多数 Java 应用程序已经在 Linux 上运行。但是,开发人员必须在 Windows 和 macOS 上进行开发期间使用 Linux 容器进行测试。

CRaC 还需要 JDK 中的支持。Azul 目前提供此类 JDK,包括免费的 OpenJDK 发行版“Azul Platform Core”和商业发行版“Azul Prime”。尽管 Azul 的副首席技术官 Simon Ritter 在 2023 年 6 月表示,他“目前不知道有任何其他发行版计划支持 CRaC”,但 Bellsoft 的 Liberica OpenJDK 发行版在 2023 年 12 月增加了对 Java 17 和 21 的 CRaC 支持。Goetz 还在比利时 Devoxx 上讨论了 CRaC:“我认为 CRaC 是一个很好的探索 […]但我并不看好它作为一种编程模型。[…]CRaC 试图以牺牲正确性为代价让事情运行得更快,我不喜欢这种权衡。

使用 CRaC,应用程序必须先运行,然后才能创建快照。理想情况下,这发生在具有类似生产负载的“预热状态”中。这可以是在 CD/CI 管道中,也可以在生产中。

最后,应用程序机密(如数据库凭据或 API 密钥)可能会在快照文件中泄露。加密快照文件可以解决此问题,但代价是写入和读取快照文件的 CPU 负载更高。Broadcom 计划在 Spring Framework 中添加恢复后配置更新功能。例如,在通过应用程序上下文刷新进行快照还原后,这可以将快照文件中的虚假数据库凭据切换为真实数据库凭据。

对 CRaC 的支持是“初始的”,因为 Broadcom 计划进一步改进,并且一些库需要更新才能关闭和重新打开其资源。Broadcom 有一个存储库,其中包含用于 Spring 项目和 CRaC 的单元和应用程序测试。其状态文件显示哪些项目通过和失败。

Spring 文档提供了有关 CRaC 的更多信息。

扩展到零的既定方法:GraalVM 原生映像

Spring 6 和 Spring Boot 3 已经有一种扩展到零的方法——GraalVM Native Image,这是一个提前 (AOT) 编译器,可为 Linux、Windows 和 macOS 创建本机可执行文件。与 CRaC 一样,它将启动时间缩短到接近于零。与 CRaC 不同,它还大大减少了内存使用量,提高了安全性,并缩小了应用程序文件大小。

在 Azure 1 CPU 2 GB RAM 云服务器上,使用 GraalVM Native Image 的 Spring PetClinic 的启动时间从 11.97 秒缩短到 200 毫秒,比 CRaC 快 10 毫秒。在 Azure 2 CPU 4 GB RAM 云服务器上,5.76 秒变成了 210 毫秒,比 CRaC 慢 80 毫秒。

Deleuze 在 Spring PetClinic 中使用 GraalVM Native Image 节省了内存。启动后的内存消耗在 JIT JVM 上为 214 MB,在 CRaC 上为 204 MB。GraalVM Community Edition 将此大小减少到 82 MB,比 JVM 低 2.6 倍。Oracle GraalVM(以前的企业版)达到了 61 MB,比 JVM 低 3.5 倍。

GraalVM 应用程序与 JVM 和 JIT 的峰值性能是一个激烈争论的话题。在 2023 年 11 月 2 日的“A Bootiful Podcast”中,Broadcom 的 Spring 开发倡导者 Josh Long 采访了 GraalVM 创始人兼 Oracle 副总裁 Thomas Wuerthinger。“上一个(Oracle GraalVM)版本中的重大新闻,”Wuerthinger说,“我们第一次可以在所有方面都击败JIT编译器配置,包括峰值吞吐量”(从34分56秒开始)。一周后,Ritter 在同一个播客中指出,“使用 GraalVM 编译代码和静态编译代码获得的最终性能水平与使用 JIT 编译代码获得的性能水平不同”(从 39 分 6 秒开始)。

GraalVM 团队在版本 21 中使用 Spring PetClinic 测量了性能。他们发现,与使用 JIT 编译器的 JVM 相比,Oracle GraalVM 的峰值吞吐量提高了 8%,即 11,902 个请求/秒,而 11,066 个请求/秒。

德勒兹测量了相同的应用。从技术上讲,他也看到 GraalVM 击败了 JVM,但领先 6% — 1,363 个请求/秒对 1,283 个请求。但是,Oracle GraalVM 从此值开始,并降级为 1,190 个请求/秒。与此同时,JIT JVM 性能稳步提高,在大约 33 秒后超过了 Oracle GraalVM,并以 1,283 个请求/秒结束,并且仍在增长。Deleuze 没有使用 Oracle GraalVM 的性能导向优化 (PGO),因此在桌面上留下了一些性能。

GraalVM 社区版以 992 个请求/秒开始,以 1,008 个请求结束,仅略有不同。CRaC 赢得了 60 秒基准测试,从 896 个请求/秒开始,到 1,500 个请求结束。没有超过 60 秒的数据可用。

就像 CRaC 一样,使用 GraalVM Native Image 也需要权衡取舍。有些 Java 功能是不可能的(例如在运行时加载任意类或创建新的类或方法),有些功能是不完整的(例如 macOS 上缺少 AWT/Java FX 支持或有限数量的 Java Flight Recorder 事件)。并非所有可观测性和测试框架都支持 GraalVM,因为 GraalVM 不允许在运行时生成动态代码,也无法运行 Java 代理。反射和类加载是可能的,但需要配置。

所有这些都会破坏 Spring 项目或第三方库。与 CRaC 一样,Broadcom 也有一个存储库,其中包含对 Spring 项目和 GraalVM Native Image 的测试。社区驱动的 GraalVM 可访问性元数据存储库具有适用于流行库的 GraalVM Native Image 配置。在 Spring 中使用时,GraalVM Native Image 会自动查询该存储库。

最后,GraalVM 的开发人员体验比 JIT Java 差;正如 Deleuze 在他的演讲中指出的那样,构建时间是“几分钟而不是几秒钟”,在 Windows 和 macOS 上调试 GraalVM 应用程序需要在 Linux 容器中编译和运行应用程序,这很耗时。Java 开发人员在开发过程中将主要使用 JIT JVM,并使用 GraalVM Native Image 构建 CI/CD 管道。但是,在添加新库或解决生产问题时,他们必须在其 PC 上使用 GraalVM Native Image。

更快的 JIT JVM 启动:Spring AOT 和类数据共享 (CDS)

Spring AOT 是负责将 GraalVM Native Image 集成到 Spring 中的项目。它的一些优化可以在没有 GraalVM Native Image 的 JIT JVM 上运行时使用。Broadcom估计,这可以将启动时间缩短约15%。Spring 文档提供了详细信息。

OpenJDK JVM 在每次启动时计算可用类及其成员的列表。OpenJDK 的 CDS 功能将该信息保存到文件中,并在以后的运行中加载它。这还可以将启动时间缩短约 15%。Spring Framework 6.1.1 通过特殊的 JVM 标志支持它。Spring Boot 目前不支持此功能,但将来可能会支持(请参阅此处和此处)。

德勒兹在春季和春季AOT上对CDS进行了深入探讨。他在 Spring PetClinic 中测量了 Spring AOT 和 CDS 组合的启动时间缩短。在 Azure 1 CPU 2 GB RAM 云服务器上,与从可执行 JAR 开始相比,此组合节省了 50%,与从解压缩的类开始相比节省了 37%。在 Azure 2 CPU 4 GB RAM 云服务器上,节省的成本分别为 53% 和 39%。他强调,“Spring Boot应用程序的生产部署应该被解压缩,以获得最佳启动时间。

其他功能和 Spring Next

更新后的 HTTP REST 客户端将 Spring 响应端的 fluent 接口与虚拟线程的使用结合在一起:

RestClient client = RestClient.create(baseUrl);
ResponseEntity<Person> person = client.get().uri("/persons/1")
    .accept(MediaType.APPLICATION JSON)
    .retrieve( )
    .toEntity(Person.class);

JDBC 客户端 API 现在也具有流畅的接口:

JdbcClient client = JdbcClient.create(dataSource);
Optional<Person> person = client.sql("SELECT .. WHERE ID = :id")
    .param("id", 3)
    .query(Person.class)
    .optional();

数据绑定和验证也得到了改进,具有更高的一致性,例如内置的 Spring MVC 处理程序方法验证或对 Java 记录的扩展支持。

Spring Boot 3.2 将获得为期一年的免费支持,直到 2024 年 11 月 23 日。商业支持将该时间表再延长 15 个月,直到 2026 年 2 月 23 日。自 2018 年 3 月的 Spring Boot 2.0 以来,这些支持时间线模式就一直存在。只有 Spring Boot 2.7 作为最后一个 2.x 版本,获得了额外的六个月免费和商业支持,刚刚随着 Spring Boot 3.2 的发布而结束,而对 2.7 的商业支持将持续到 2025 年 8 月 24 日。

Spring Boot 3.2 删除了 Spring Boot 3.0 弃用的类、方法和属性。Spring Boot 3.2 还更新了许多依赖项,例如 Spring Security 6.2、Kotlin 1.9、Hibernate 6.3 和 Kafka 4.6。Spring Boot 3.2 和 Spring Framework 6.1 发行说明全面概述了所有更改。

Spring Framework 6.2 预计将于 2024 年 11 月推出,将支持 Jakarta EE 11。该版本包括更新的 API,例如 Jakarta Servlet 6.1 和 Jakarta Persistence 3.2。当使用 Spring 的高级抽象(如 Spring Data)时,这些依赖项更新大多是不可见的。@RestController

Spring 还宣布与 Project Leyden 合作开展一个名为“pstay optimizations”的项目。正如德勒兹所说,这些优化是“类固醇上的CDS + AOT缓存”。现在还处于早期阶段,但 Broadcom 预计启动时间会快 2-4 倍,而且几乎没有权衡。


https://www.xamrdz.com/backend/3jp1848618.html

相关文章: