抢占先机
VMware 提供培训和认证,助您加速进步。
了解更多Spring 团队刚刚发布了 spring-graalvm-native 项目的 0.6.0 版本。该项目旨在让任何尝试构建 Spring 应用的 GraalVM 原生镜像的人更容易进行。
要深入了解 Spring 原生镜像,请参阅Sébastien Deleuze 的 Devoxx 演讲。
在这篇博文中,我们将讨论此后发生的变化,并向您指出一些关键资源,让您可以尝试!该项目位于 spring-projects-experimental github 组织下,表明它正在进行中,但我们提供了许多示例应用程序,展示了已经可以工作的各种技术,还有大量关于如何使用您自己的应用程序进行实验的文档。
简单回顾一下,GraalVM 是一个涵盖多种用途的伞形项目,但我们在此关注的关键方面是将 JVM 代码作为原生镜像运行。一旦编译成特定平台的原生镜像,应用程序应该具有非常快的启动速度和更可靠的内存特性(没有 JIT 在开始时导致内存尖峰)。
创建镜像时,原生镜像构建工具需要了解有关应用程序的信息,例如加载了哪些资源,可能通过反射访问哪些类型,以及类型是否可以在镜像构建时安全地初始化或必须在运行时稍后初始化。这些信息使原生镜像工具能够尝试为应用程序构建一个最优化的镜像。
实际上有几种收集和传递这种配置的方法
这些计算配置的方法各有优缺点。例如,agent 只能收集应用程序运行期间被执行的代码路径信息,但它肯定会创建一套精确的、最优化的所需配置(在资源/反射访问方面)。另一方面,Feature 不会创建一个完全最优化的配置,因为它不运行应用程序,所以必须允许某些可能或可能不会执行的代码路径,但作为构建过程的一部分,Feature 可以进行 Spring 特有的优化,例如提前评估条件配置。当原生镜像构建运行时,整个类路径是已知的,因此此时可以执行 @ConditionalOnClass
检查,如果检查失败,则可以丢弃该配置,甚至在生成的镜像启动时也不会考虑它。
我们一直在这些方面努力工作,试图改善生态系统,以便我们可以走向一个一切都能正常工作的世界。还有一些路要走!我们正在深入研究 Tomcat,使其配置像 netty 一样易于获取。与 GraalVM 团队合作,我们一直在确保 Spring Boot 应用程序中没有什么会阻碍原生镜像构建(需要在双方进行修复),并改进 spring-graalvm-native feature,使其更好地理解更广泛的 Spring 应用程序。我们还一直在帮助确保 agent 收集器没有遗漏任何东西。我们目前正在与 GraalVM 团队解决的 issues 实际跟踪在此。
自 Spring One Platform 2019 和 Devoxx 的演示以来,该 feature 对 Boot 的了解更多了,agent 遗漏的东西更少了,GraalVM 的兼容性更好了,生成的镜像尺寸更小了,镜像构建时间也缩短了,我们还包含了更多展示已工作内容的示例项目。
有许多示例项目在此,甚至包括 PetClinic(当然!),以及与示例相关的文档在此,说明如何使用它们。有使用 Netty、Tomcat、Spring MVC、Spring WebFlux、JPA、Spring Cloud Function、kotlin 等的示例。您可能会看到什么?
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::
...
INFO: Started TomcatApplication in 0.044 seconds (JVM running for 0.62)
要将其应用于您自己的项目,文档描述了使用feature、使用agent,或使用两者结合的混合模式的所有步骤。混合模式有时是两全其美的选择,因为 agent 可以捕捉到 feature 可能遗漏的内容,反之亦然。
整个过程还没有非常顺畅,许多领域还需要更多工作。负责 feature 的团队和 GraalVM 团队一直在努力改进诊断功能,以便在出现问题时您仍然能够取得进展并了解下一步该怎么做。某个随机应用程序第一次就能工作是不太可能的,但对于那些致力于解决问题的人来说,许多应用程序都可以工作。您的应用程序可能使用了我们测试中尚未遇到的库。它可能使用了 feature 尚未了解的某些 Spring 行为。问题可能在镜像构建时出现,也可能在编译后的镜像启动时的运行时出现。此处有一个故障排除页面,讨论了一些常见问题以及如何解决它们。遇到其他问题?请在项目上提交 issue。
在 spring-graalvm-native 项目内部有一个配置子项目,它试图以易于扩展的形式封装关于 Spring Boot 行为的知识。例如,它记录了一个特定的导入选择器可能需要反射访问特定的类型。feature 本身由这种封装的知识驱动,如果您发现目前的知识不足,请随时增强它并贡献回项目以构建这些知识,请参阅扩展性指南。
spring-graalvm-native 项目中还包含一些 substitution(替换),substitution 是 GraalVM 的一个术语,指在镜像构建时对现有类进行更改,因为该类在原生镜像中包含时当前运行不佳。随着时间的推移,计划仍然是消除这些 substitution,并与包含这些问题类的项目合作,使它们达到一种理想的形式,无论是在原生镜像内部还是外部都能正常工作。
虽然 spring-graalvm-native 专注于 Spring,但显然一个 Spring 项目通常会包含许多第三方依赖。其中许多还没有包含必要的配置,因此我们的 feature 正在尽力“弥补”它们。只要有可能,我们的计划仍然是与这些依赖提供者合作,帮助他们制作理想的原生镜像配置,然后原生镜像构建就会自动读取这些配置。GraalVM agent 提供了一种很好的方法来尝试处理缺少配置的代码。
所有工作都只在这个实验性 feature 中进行吗?远非如此。Spring 中已经添加了许多增强功能,以确保在构建为原生镜像时能正常运行。例如,在 Spring Framework 中,Spring Framework 5.2 的 @Configuration
的 proxyBeanMethods
属性使得应用程序无需 CGLIB 代理即可运行(原生镜像过程只支持 JDK 代理)。我们还在 Spring Boot 条件处理中重构了一些类加载,使用了不同的方法,因为 agent 无法捕捉到原来的加载形式。
更多此类增强功能将陆续推出。feature 还有更多需要学习的地方,同时在核心 Spring 中,仍有太多工作是在启动时完成的,我们可以将其推到构建时进行,这将对构建的原生镜像的内存需求产生重大影响。这些改进不仅将惠及构建为原生镜像的应用程序,也将惠及在常规 JVM 上运行的应用程序。情况只会越来越好!感谢 GraalVM 团队在此项工作中给予我们的支持。要跟踪我们的进展,请关注该项目。