Spring真的能够干掉JVM吗?

图片原文:spring.io/blog/2021/03/11/announcing-spring-native-beta

Spring团队最近发布了Spring Native的Beta版本,这一新功能允许Spring应用程序通过GraalVM原生映像的方式进行运行。为了更好地实现原生运行,Spring Native提供了Maven和Gradle插件,并引入了优化原生配置的注解。

Spring Native 的Beta版现已上线,并且可以直接在http://start.spring.io上进行体验。

这一发布意味着,自Spring成立以来,除了支持常规Java虚拟机外,Spring团队还引入了Beta版本支持,用户可以通过GraalVM将Spring应用程序编译为本机映像。此功能支持Java和Kotlin。

这些本机Spring应用程序可以作为独立的可执行文件进行部署(无需安装JVM),并具备诸多优点,例如几乎即时启动(通常小于100毫秒)、即时峰值性能及更低的内存消耗,同时所需的构建时间和运行时优化次数也少于传统JVM。

图片
通过简单的命令mvn spring-boot:build-imagegradle bootBuildImage,您能够生成一个经过优化的容器映像,该映像将包含一个最小的操作系统层和一个小的本机可执行文件,仅附带JDK、Spring及您在应用程序中使用的必需依赖项。下面是一个示例,展示了一个包含Spring Boot、Spring MVC、Jackson、Tomcat、JDK和应用程序的50MB可执行文件的最小容器映像。

图片

这种原生方式在多个场景中为Spring应用提供了显著价值:

  • 具备Spring Cloud功能的无服务器架构
  • 更经济且可持续地托管Spring微服务
  • 非常适合像VMware Tanzu等Kubernetes平台
  • 想创建最佳的容器映像来打包您的Spring应用和服务

在应用场景方面,Piotr Mińkowski提供了一份精彩的指南,介绍了如何在Knative上使用Spring Boot和GraalVM构建原生微服务。

团队合作


Spring Native Beta是Spring团队与其家族项目广泛合作的结果,涵盖了Spring Framework、Spring Boot,以及Spring Data、Spring Security、Spring Cloud和Spring Initializr等。

原生功能的工作范围超出了Spring本身,因为原生运行涉及更广泛的JVM生态系统,因此官方与GraalVM团队合作,旨在改善原生映像的兼容性和资源消耗。

据GraalVM团队的Vojin Jovanovic表示:“与Spring团队共同构建原生JVM生态系统是一次愉快的经历:他们的深厚技术背景和对社区的敏锐关注总是能带来最佳解决方案。最新的Spring Native版本及其在JVM生态系统中的多种用途为原生编译的广泛采用铺平了道路。”

支持范围


随着Spring Native从Alpha版本过渡到Beta版本,明确我们提供的支持范围至关重要。

Alpha版本是第一步,我们进行了大量实验,完善了Spring Native(之前称为Spring GraalVM Native)的架构,兼容性并对一系列样本进行了重大更改的封装。我们还向GraalVM团队报告了许多修复问题,以缩小JVM与Spring应用程序本机之间的差距。

虽然Beta版本仍被视为实验性,但这标志着Spring现在为Spring生态系统的一个子集提供了本机的支持。如果项目使用的是支持的依赖项,用户可以尝试在项目上进行测试;如出现问题,可以报告错误或提出请求。每次Spring Boot 2.x次要版本的修补程序版本都会发布一个新版本的Spring Native。Spring Native 0.9.0支持Spring Boot 2.4.3,Spring Native 0.9.1支持Spring Boot 2.4.4等等。尽管可能会有一些重大变化,但我们将记录迁移路径。文档质量已达到一个新水平:参考文档以HTML单页或PDF形式提供,并且我们发布了本机提示的Javadoc公共API。

start.spring.io


Stéphane Nicoll在http://start.spring.io和相关IDE的集成中引入了对Spring Native的支持,因此现在这是探索如何使用Spring构建原生应用的最简单方式。

图片
添加Spring Native依赖后,Maven或Gradle项目会自动配置所需的依赖项和插件,以支持原生功能,应用代码本身无需修改。

请检查自动生成的HELP.md文件,该文件包含有用的链接和文档,同时标记您所选择的某些在原生环境下不支持的依赖项。

提前转换


本机环境与JVM存在差异:类路径在构建时是固定的,诸如反射或资源配置等需求需要在构建时就确定,没有类的延迟加载(可执行文件中附带的所有内容在启动时都加载到内存中),并且可以调用某些代码来进行构建时的处理。

为了确保Spring应用程序在本机上以最大兼容性和最小占用空间运行,Brian Clozel在此版本中引入了Spring提前(AOT)Maven和Gradle插件,允许在应用程序上提前执行转换。

这种转换是基于由Andy Clement设计和实现的推理引擎,旨在生成GraalVM本机配置(反射、资源、代理、本机映像选项),该引擎了解Spring的编程模型和基础架构。例如,对于每个使用@Controller注解的类,会添加一条记录到生成的reflect-config.json文件中。

有些本机配置无法推断,对于这些情况,我们引入了本机提示注解(有关更多详细信息,请参见Javadoc),使Spring Native能以更可维护的、类型安全的和灵活的方式支持本机配置,优于基于常规JSON的本机映像配置。例如,同Spring本地MySQL驱动的支持提供了线索,以确保生成本机映像时正确生成reflect-config.jsonresource-config.jsonnative-image.properties条目,如下所示:

@NativeHint(  
    trigger = Driver.class,  
    options = "--enable-all-security-services",  
    types = @TypeHint(types = {  
       FailoverConnectionUrl.class,  
       FailoverDnsSrvConnectionUrl.class,  
       // ...  
    }), resources = {  
    @ResourceHint(patterns = "com/mysql/cj/TlsSettings.properties"),  
    @ResourceHint(patterns = "com.mysql.cj.LocalizedErrorMessages",  
                      isBundle = true)  
})  
public class MySqlHints implements NativeConfiguration {}  

NativeConfiguration和其他动态配置机制允许更强大的和动态的配置生成,但请注意,其API将在即将到来的版本中发生显著变化。

Spring开发人员还可以使用特定于应用程序的本机提示直接注释其@Configuration@SpringBootApplication类,例如,Book类通过RestTemplateWebClient编程API将类序列化为JSON:

@TypeHint(types = Book.class)  
@SpringBootApplication  
public class WebClientApplication {  
    // ...  
}  

与提前转换系统结合使用时,最强大的机制是利用Spring Boot部署模型与GraalVM原生的结合,引入的封闭世界假设以自动生成本机优化代码(源代码和字节码)。此处的目标是通过使用本机映像编译器可以直接分析的代码构造来限制所需的额外本机配置,提高兼容性,同时减少反射所需的配置数量,从而降低占用空间、资源和代理。例如,提前转换spring.factories(Spring Boot的扩展机制)到优化的程序设计版本,以避免反射,并在应用程序上下文中过滤掉不必要的条目。

这是Spring AOT的初步尝试,未来我们计划在@Configuration功能配置中添加更强大的转换,通过提前分析替换运行时反射,而这种提前分析将自动生成配置类,使用lambda和方法引用等程序化构造。这将使GraalVM本机图像编译器能直接了解Spring配置,无需任何反射配置或*.class资源。

一个关键点是,在使用Spring Native时,默认情况下在JVM上也会使用AOT生成的代码,以允许您在JVM上进行短反馈循环,利用“本机友好的代码路径”。您的调试器和所有常规工具也可以使用。

尽管Spring AOT转换目前主要由本机需求驱动,但其中许多功能并不是特定于本机的,因此其中一些也可能提供优化,以在JVM上运行Spring Boot应用程序。与此类主题相关的数据驱动分析对于我们衡量效率和性能做出决策至关重要。

我们可能会改进IDE集成,现在确保在IDE中运行应用程序之前,请务必阅读相关文档,以便进行潜在的手动配置步骤,以更新生成的源代码。

结论


Spring本地化战略有两个主要支柱。首个支柱是使Spring基础架构适应本机环境,而无需对数百万个现有Spring Boot应用程序进行重大更改。这包括我们在Spring顶级项目中所做的更改,以使其更适合本机,如@NativeHint和Spring Native中成熟的基础架构,以及Spring AOT构建插件。

第二个支柱比Spring本身更广泛,原生环境是一个具有不同特性的独立平台,但Java生态系统需要尽可能保持一致,以避免出现两种截然不同的Java风格,这将导致维护困难。因此,我们与GraalVM团队进行了深入合作,旨在缩小这两者之间的差距。在接下来的几个月里,这项合作将专注于改善更广泛的JVM生态系统的本机测试和本机配置。