Spring 在今年 3 月份推出了 Spring Native Beta 版本,我本来还想着等正式发布了再研究下,不用等了,现在我们就来尝尝鲜。
https://spring.io/blog/2021/03/11/announcing-spring-native-beta
Spring Native 简介
我们都知道,传统的 Spring 应用程序都是必须依赖于 Java 虚拟机(JVM)运行的,Spring Native 的诞生就是无需 JVM,它提供了另外一种运行和部署 Spring 应用的方式(目前只支持 Java 和 Kotlin),通过 GraalVM 将 Spring 应用程序编译成原生镜像。
Spring Native 特点
1、无需 JVM 环境, Spring Native 应用程序可以作为一个可执行文件独立部署;
2、应用即时启动,一般情况下应用启动时间 < 100ms;
3、即时的峰值性能;
4、更少的内存消耗;
Spring Native 缺点
Spring Native 应用启动那么快也是有代价的,和 JVM 应用相比:
1、构建更笨重、构建时间更长;
2、更少的运行时优化;
3、很多 Java 功能受限;
4、很多特性还很不成熟;
Spring Native 应用场景
1、Spring Cloud 无服务器化(Serverless);
2、以更廉价持久的方式运行 Spring 微服务;
3、非常适合 Kubernetes 平台,如:VMware Tanzu;
4、为 Spring 应用创建更佳的容器镜像;
Spring Native 和 JVM 的区别
1、Spring Native 构建时会进行应用程序静态分析;
2、Spring Native 构建时会移除未被使用的组件;
3、Spring Native 反射、资源、动态代理需要配置化;
4、Spring Native 构建时的 classpath 是固定不变的;
5、Spring Native 没有类延迟加载,可执行文件包含所有内容都在启动时加载到内存;
6、Spring Native 构建时会运行一些代码;
7、Spring Native 对于 Java 应用程序还存在一些局限性;
GraalVM 简介
Spring Native 的核心就是 Oracle 的黑科技: GraalVM。
GraalVM 是一个由 Oracle 开发的全栈通用虚拟机,拥有高性能、跨语言交互等逆天特性,不仅支持了 Java、Scala、Groovy、Kotlin 等基于 JVM 的语言,以及 C、C++ 等基于 LLVM 的语言,还支持其他像 JavaScript、Ruby、Python 和 R 语言等,可提高多种语言的运行速度和吞吐量。
GraalVM 有以下几个特性。
具体就不介绍了,阅读我之前分享的这篇文章:Oracle 发布了一个全栈虚拟机 GraalVM
重点来看原生镜像功能:
$ javac HelloWorld.java
$ time java HelloWorld
user 0.070s
$ native-image HelloWorld
$ time ./helloworld
user 0.005s
GraalVM 可以预编译成原生镜像,从而极大提速了启动时间,并能减少 JVM 应用的内存占用。现在你知道为什么 Spring Native 启动那么快的原因了!
Spring Native 正是通过 GraalVM 提供了对传统 Spring 应用程序的轻量级运行方式,在不用修改任何传统应用程序代码的情况下,通过集成 Spring Native 项目就能轻松实现。
开始尝鲜
构建 Spring Native 应用的两种方式:
1、使用 Spring Boot Buildpacks 来生成一个包含原生可执行文件的轻量级容器;
2、使用 GraalVM native image Maven 插件来生成一个包含原生可执行文件;
本文使用第一种方式进行尝鲜!
1、环境要求
这种方式需要安装 Docker 环境:
- Linux 需要配置非 root 用户可运行
- Mac 需要配置最大内存为 8G 或以上
因为我本地已经装好了,这里不再演示了,不会的点击这里阅读参考一下,或者关注公众号:Java技术栈,在历史文章中搜索阅读。
2、添加依赖
Spring Native 在 start.spring.io 上面已经可以开始使用了,在页面上添加一个 "Spring Native" 依赖进去就好,如下所示:
Spring Boot:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/>
</parent>
Spring Native:
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>${spring-native.version}</version>
</dependency>
</dependencies>
注意依赖版本:
Spring Native 最新版本为:0.9.2,只支持 Spring Boot 2.4.5
3、添加 Spring AOT 插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>0.9.2</version>
<executions>
<execution>
<id>test-generate</id>
<goals>
<goal>test-generate</goal>
</goals>
</execution>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Spring AOT 插件执行所需的提前转换,以提升原生镜像的兼容性。
4、开启原生镜像支持
在 Spring Boot Maven 插件中增加以下配置:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder:tiny</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
</env>
</image>
</configuration>
</plugin>
5、添加 Maven 仓库支持
Spring Native 依赖和插件需要在 Spring 仓库中下载,需要添加以下配置。
<repositories>
<repository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
</pluginRepository>
</pluginRepositories>
如果不能正常下载 Native 依赖和插件,需要检查 Maven 的 settings.xml 文件:
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*,!spring-release</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
把 mirrorOf 值由 * 修改为:*,!spring-release
6、添加测试接口
/**
* 微信公众号:Java技术栈
*/
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
@RequestMapping("/native/hi")
@ResponseBody
public String hiNative() {
return "hi native application...";
}
}
本文所有代码已上传至:https://github.com/javastacks/spring-boot-best-practice
7、构建原生应用
Maven 插件构建命令:
mvn spring-boot:build-image
这个会创建一个 Linux 容器,使用 GraalVM 原生镜像编译器构建出原生应用程序,容器镜像默认只安装在本地。
在 IDEA 插件中运行:
配置好后开始构建:
会看到大量这样的错误,不用理会,这个会在未来移除。
最终构建完成,一个简单的 Spring Boot 应用程序,这个构建却过程花了我 4 分钟。。
8、运行原生应用
使用平常运行 Docker 镜像的方式就能运行原生应用:
docker run --rm -p 8080:8080
当然也可以在项目中编写 docker-compose.yml
文件的方式,这里不再演示,感兴趣的可以关注公众号:Java技术栈,在历史文章中搜索阅读 Docker 系列文章。
一般情况下,运行原生应用程序只需要 100 毫秒以下,而运行基于 JVM 的应用程序大概需要 15 秒左右。
事实是否如此呢,一起来看看!
我天,82 毫秒就启动了,启动确实快。
再来访问我们之前写的接口:
输出正常,原生应用验证完成。
另外,在 target 目录中也生成了可执行的 jar 包:
然后我们用传统 JVM 环境来运行下:
java -jar spring-boot-native-1.0.jar
启动时间:1.903 秒,虽然看起来差距不大,但原生应用启动时间(0.082 秒)也比 JVM 快了 23 倍,在不同的代码量面前可能会有较大差距的体现。
当然这只是我测试的参考时间,但可以说明的原生应用运行确实要比 JVM 快不少!
我们再来比对下包的大小
查看刚生成的 Docker 镜像:
docker image ls
查看基于 JVM 的可执行 jar 包:
Docker 镜像大小:80.7 M,而基于 JVM 运行的可执行 jar 包却只有不到 20M。
这是因为原生镜像不仅包含了应用程序中所使用到的来自 JDK、Spring 中的必须项,还包含了一个最小化的 OS 系统层,所以肯定是要比之前的要大不少。
总结
本文介绍了 Spring Native 的特点,及演示了基于 Docker 镜像的原生应用。
感兴趣的都可以 Star 下该仓库,包含了之前写的 Spring Boot 教程及示例源码。
当然除了基于 Docker 镜像,还可以使用原生镜像 Maven 插件的方式,那种方式不需要 Docker,但需要安装原生镜像编译器 GraalVM,道理是一样的,这里就不再演示了,有兴趣的可以参考:
https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/#getting-started-native-image
如果有使用 Docker,那第一种肯定是更好的方式,所有的依赖都打包到一个镜像中了,避免了环境污染。
最后总结一下就是,Spring Native 可以无需 JVM 运行,构建慢、启动快、内存占用少、运行优化少,另外还有很多 Java 特性受限,比如:反射、动态代理等都需要通过提前配置化,因为 Java 是一种动态链接的语言,原生应用都要提前编译,这个像反射、动态代理这种特性就会受限。
另外,目前 Spring Native 还处于 Beta 测试版本,现阶段肯定还会存在很多问题,未来可能也还会有变更,不过我会继续关注的,后续我也会更新更多 Java 系列最新技术实战文章,公众号Java技术栈第一时间推送。请大家持续关注哦!
本节所有内容都是参考官网最新文档,可谓是做了第一个吃螃蟹的人,觉得我的文章对你用收获的话,动动小手,给个在看、转发,原创不易,栈长需要你的鼓励。
版权申明:本文系公众号 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重大家的劳动成果和知识产权,抄袭必究。
近期热文推荐:
1.600+ 道 Java面试题及答案整理(2021最新版)
2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!
3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!
4.Spring Cloud 2020.0.0 正式发布,全新颠覆性版本!
觉得不错,别忘了随手点赞+转发哦!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。