初识spring native
官方介绍
Spring Native提供了使用GraalVM 本机图像编译器将Spring应用程序编译为本机可执行文件的支持。
与Java虚拟机相比,本机映像可以为许多类型的工作负载提供更便宜,更可持续的托管。这些包括微服务,功能工作负载,非常适合容器和Kubernetes
使用本机映像具有关键优势,例如即时启动,即时峰值性能和减少的内存消耗。
GraalVM本机项目希望随着时间的推移会改善一些缺点和折衷方案。构建本机映像是一个繁重的过程,比常规应用程序要慢。预热后,本机映像具有较少的运行时优化。最后,它比具有某些不同行为的JVM还不成熟。
常规JVM和此本机映像平台之间的主要区别是:
- 在构建时将未使用的零件删除。
- 反射,资源和动态代理需要配置。
- 类路径在构建时是固定的。
- 没有类延迟加载:可执行文件中附带的所有内容都将在启动时加载到内存中。
- 一些代码将在构建时运行。
- 围绕Java应用程序的某些方面存在一些局限性,这些局限性未得到完全支持。
简单来说,就是更快.更短,更小
十分适用于目前互联网环境的快捷开发和微服务架构的项目
而Spring Native的基础则是Graalvm,一个由oracle开发维护的多语言编译/运行时平台.
它的官方说法是高性能JDK发行版
,目前已支持到7种语言,包括不仅限于java.ruby.node等
基于graalvm开发的java框架还有一个国内目前还不算太火的Quarkus,在一些油管up的测评视频中,证明quarkus(1.13)要比spring native(0.7x)更快更小,我也写了quarkus快一年了,即使抛弃graalvm本身,也确实比springboot要更快,更短
代码部分
我用的是0.92,仅支持springboot2.4.5
还有,机器的内存最好备到8个G,因为我测试时候memory in use一度飙升到5个多G,这可能也是graalvm为了效率更高付出的代价吧
pom
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.5</version> <relativePath/> </parent> <groupId>org.xiaowu</groupId> <artifactId>behappy-springboot</artifactId> <version>0.0.1-SNAPSHOT</version> <name>behappy-springboot</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-native.version>0.9.2</spring-native.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-native</artifactId> <version>${spring-native.version}</version> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <classifier>exec</classifier> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> <image> <builder>paketobuildpacks/builder:tiny</builder> <env> <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE> </env> </image> </configuration> </plugin> <plugin> <groupId>org.springframework.experimental</groupId> <artifactId>spring-aot-maven-plugin</artifactId> <version>${spring-native.version}</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> <profiles> <profile> <id>native-image</id> <build> <plugins> <plugin> <groupId>org.graalvm.nativeimage</groupId> <artifactId>native-image-maven-plugin</artifactId> <version>21.0.0.2</version> <configuration> <mainClass>org.xiaowu.behappy.BehappySpringbootApplication</mainClass> <buildArgs> <arg>--static</arg> </buildArgs> </configuration> <executions> <execution> <goals> <goal>native-image</goal> </goals> <phase>package</phase> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles> <repositories> <repository> <id>spring-releases</id> <name>Spring Releases</name> <url>https://repo.spring.io/release</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-releases</id> <name>Spring Releases</name> <url>https://repo.spring.io/release</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </project>
|
测试Controller
1 2 3 4 5 6 7 8 9 10 11
|
@RestController public class TestController {
@GetMapping("/") public String test() { return "TestController"; } }
|
linux环境下安装各种环境(windows需要安装各种c的运行库,安完还是缺...linux要好很多)
graalvm安装
1 2 3 4 5
| 使用SDKMan安装适当的SDK和GraalVM插件,SDKMan是Java SDK管理工具,可让轻松安装和配置GraalVM所需的依赖项(截至当日,支持8和11) curl -s "https://get.sdkman.io" | bash sdk install java 21.0.0.r8-grl sdk use java 21.0.0.r8-grl gu install native-image
|
gcc,unzip,zip,glibc-static,zlib
1 2 3 4
| yum install -y glibc-static zlib zlib-devel zip unzip sudo yum group install "Development Tools" 该命令安装了一堆新的软件包,包括gcc,g++和make。 gcc --version
|
构建
然后spring native提供了两种构建方式
将Spring Native应用程序构建到Docker映像中
1 2 3 4
| 运行以下命令以构建Docker映像文件,并将编译后的代码包装在构建包中。 mvn spring-boot:build-image 使用Docker运行映像: docker run -p 8080:8080 docker.io/xxx
|
将Spring Native应用程序构建到可执行文件中
1 2 3 4
| 运行以下命令编译Spring Boot应用程序的本机二进制可执行文件: mvn clean -Pnative-image package 通过运行以下命令来执行本机应用程序: target/spring-native-example
|
横向对比
这里我启动的是可执行文件
速度可以说是相当快了
接下来给他做成docker容器再看下
1 2 3 4 5 6
| FROM scratch COPY target/org.xiaowu.behappy.behappyspringbootapplication /app ENTRYPOINT ["/app"]
docker build -f Dockerfile -t behappy:1.0 . docker run -p 8080:8080 --name springnative -d behappy:1.0
|
再贴两张youtube上某up做的对比图
项目仅加了一个webflux
前者是native的,后者是非native的,
可以看到size是有明显缩小的
上图的启动时间是3秒
下图可以看到0.1秒多,spring native官方介绍过,正常的一般启动都不会超过100毫秒,可以看出启动时间上的差距是巨大的
右面是两者的cpu消耗,io占用等信息,也能看出两者有着明显的差距
更新:SpingBoot3(时隔两年的第二次测试)
随着springboot3的发布,原spring native项目已被其包含在内(取代),现在可以直接使用
参考:
- https://news.sangniao.com/p/3379643754#Spring+Boot+3%E4%B8%AD%E7%9A%84Native+Image%E6%94%AF%E6%8C%81
- action workflow资源监控:https://github.com/runforesight/workflow-telemetry-action
- 问题解决:ERROR: Please rebuild the executable with an appropriate setting of the -march option.
- springboot - GraalVM 原生镜像(Image)支持
代码地址
因为构建需要太多资源,我这里直接使用github的actions
https://github.com/behappy-java-study/customerservice-spring-boot-3
orginal分支对应非native
版本
native分支对应native
版本
横向对比
构建消耗资源
CPU占用
内存占用
IO
最终构建完成时间
- 非native(不到3分钟,当然这个时间不稳定,但也基本维持在3-5分钟之间)
容器启动时间
启动容器
docker run --name spring-boot-3-native -d -p 8501:8500 wangxiaowu950330/customerservice-spring-boot-3:0.0.1-SNAPSHOT-native
docker run --name spring-boot-3-original -d -p 8502:8500 wangxiaowu950330/customerservice-spring-boot-3:0.0.1-SNAPSHOT-original
1 2 3 4 5 6 7 8 9 10 11
| [root@development1 ~] 2023-07-28T09:00:12.700Z WARN 1 --- [ main] org.hibernate.orm.deprecation : HHH90000021: Encountered deprecated setting [javax.persistence.sharedCache.mode], use [jakarta.persistence.sharedCache.mode] instead 2023-07-28T09:00:12.848Z INFO 1 --- [ main] SQL dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect Hibernate: drop table if exists customer cascade Hibernate: create table customer (id uuid not null, inserted_at timestamp(6), inserted_by varchar(255), updated_at timestamp(6), updated_by varchar(255), version bigint, first_name varchar(255), last_name varchar(255), primary key (id)) 2023-07-28T09:00:13.998Z INFO 1 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] 2023-07-28T09:00:14.010Z INFO 1 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2023-07-28T09:00:14.321Z WARN 1 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 2023-07-28T09:00:14.902Z INFO 1 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 13 endpoint(s) beneath base path '/monitoring' 2023-07-28T09:00:15.057Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8500 (http) with context path '' 2023-07-28T09:00:15.103Z INFO 1 --- [ main] c.g.w.s.c.CustomerserviceApplication : Started CustomerserviceApplication in 6.581 seconds (process running for 7.039)
|
- native(依旧很快,毫秒级别,大概200毫秒启动完成)
1 2 3 4 5 6 7 8 9 10 11
| [root@development1 ~] 2023-07-28T08:48:52.085Z INFO 1 --- [ main] SQL dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect Hibernate: drop table if exists customer cascade Hibernate: create table customer (id uuid not null, inserted_at timestamp(6), inserted_by varchar(255), updated_at timestamp(6), updated_by varchar(255), version bigint, first_name varchar(255), last_name varchar(255), primary key (id)) 2023-07-28T08:48:52.100Z INFO 1 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] 2023-07-28T08:48:52.101Z INFO 1 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2023-07-28T08:48:52.138Z WARN 1 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 2023-07-28T08:48:52.177Z INFO 1 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 13 endpoint(s) beneath base path '/monitoring' 2023-07-28T08:48:52.187Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8500 (http) with context path '' 2023-07-28T08:48:52.187Z INFO 1 --- [ main] c.g.w.s.c.CustomerserviceApplication : Started CustomerserviceApplication in 0.224 seconds (process running for 0.233) 2023-07-28T08:58:47.968Z WARN 1 --- [ice housekeeper] com.zaxxer.hikari.pool.HikariPool : customerservice - Thread starvation or clock leap detected (housekeeper delta=4m25s796ms902?s592ns).
|
压测
- 因为非native到达850-900线程数时,吞吐量已基本处于下跌状态且开始出现报错,所有姑且当最高承受线程数(压测线程数)为:800
- 压测接口行为为 插入数据,当表数量到达10万时截图并观察资源占用及吞吐量
吞吐量
cpu占用/内存占用/io
结论