什么是Spring Native?
在减少Spring应用程序fat部署工件的大小方面有困难吗?在这个云时代,它成为了一个真正的问题,特别是在考虑微服务或serverless无服务器架构时。了解Spring Native如何打出本垒打并将Spring放回游戏中。
Spring Native | Beta 测试版
Spring Native能够将那些胖Spring jar的大小显著缩小为独立的可执行文件,称为本机映像。所有这些都是通过GraalVM本机映像编译器执行的,该编译器通过静态分析在构建时执行一整套优化。稍后再谈。
使用springnative,我们可以获得毫秒级的启动时间,提高了性能,并大大减少了内存占用。在Beta版中已经是这样了,所以期待更多的改进。
Spring Native解决的问题
多年来,Spring应用程序一直在缓慢膨胀,不幸的是,它不是最适合微服务和serverless无服务器体系结构的应用程序。在容器化环境中,它们往往太大、太慢,尤其是与其他语言相比。
这是我们都不想谈论的问题。我们热爱Java/Spring,并为此投入了大量资金,但事实是,许多公司/开发人员都因此考虑了其他选择。
SpringNative正在进行救援,刚刚发布了一个测试版。大满贯就要来了!
Spring Native项目一直是(现在仍然是)一项巨大的事业。其原因与Spring和GraalVM的行为方式不同有关。尤其是在构建时间和运行时行为方面。Spring执行的运行时决策本质上是非常动态的,比如创建代理、延迟加载等等。。
另一方面,GraalVM需要在构建时将所有内容提前(AOT)…并在加载应用程序之前将类路径设置为stone。那么,GraalVM和Spring团队是如何弥合这一差距的呢?
Spring开发了一个Spring AOT(提前)解析器来创建配置信息文件,GraalVM编译器需要这些文件来执行到二进制文件的转换。Spring AOT解析器是通过maven或gradle插件提供给我们的,稍后我将向您展示(仅maven)。
当我们开始部署微服务和/或无服务器架构时,这是一个巨大的问题。我们需要快速启动时间和低内存占用。这将提高主机上的应用程序密度,提高性能,降低按需使用资源的成本,甚至能够满足某些云提供商对最大内存消耗或有效负载大小等方面施加的无服务器配额限制。
所以基本上,Spring Native现在让我们回到了游戏中,因为我们能够与其他技术竞争,特别是在涉及容器化的部署架构方面。
Spring Native示例|
从现在起,您可以通过start.Spring.io
和Spring Boot version 2.4.4
自己开始试验Spring Native,因为Spring Native已经可以在dependencies列表中进行选择。
让我们试驾一下,然后git从GitHub克隆Spring GraalVM本机示例:
git clone https://github.com/spring-projects-experimental/spring-native.git
进入samples目录后,将petclinicjdbc项目导入IDE。
首先要看的是pom.xml
文件,其中包含这些新的实验依赖项和maven插件。我只介绍了与Spring Native相关的内容。
您可以看到,我们从一个不同的父pom依赖开始,它是基于本机的-实验性的。一个值得注意的构建插件是我前面提到的Spring AOT解析器。这个插件在创建GraalVM所需的配置文件以执行对本机映像的编译时非常关键。
不那么明显的是实验性的tomcat服务器。它已经针对Spring本机部署进行了优化。它将从最终的容器图像中删除3.5M的内存。这些东西加起来!
<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native-sample-parent</artifactId>
<version>0.9.2-SNAPSHOT</version>
<relativePath>../maven-parent/pom.xml</relativePath>
</parent>
..........
.......... omitted on purpose for clarity
..........
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
</dependency>
..........
.......... omitted on purpose for clarity
..........
<dependency>
<groupId>org.apache.tomcat.experimental</groupId>
<artifactId>tomcat-embed-programmatic</artifactId>
<version>${tomcat.version}</version>
</dependency>
..........
.......... omitted on purpose for clarity
..........
<build>
<plugins>
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<configuration>
<removeYamlSupport>true</removeYamlSupport>
</configuration>
</plugin>
..........
.......... omitted on purpose for clarity
..........
在IDE中打开一个终端窗口来构建和运行打包在轻量级容器映像中的本机应用程序。
mvn clean package spring-boot:build-image
您会注意到,Spring-native构建比基于Spring-JVM的构建花费的时间要长得多。同样,这是由于GraalVM优化不是免费的。你在构建时要付出代价,所以你必须耐心
在构建过程结束时,Spring-native映像被复制到Docker映像中——由Maven或Gradle插件执行。Spring在插件中使用buildpacks,这是使用Dockerfile的另一种方式,而且非常紧凑/优化。
除此之外,docker映像不包含JVM,因为本机映像不需要它——这导致运行时的容器非常轻量级。该构建创建以下docker映像“petclinic”-jdbc:0.0.1-SNAPSHOT”.
我的Spring-native构建持续了6:37分钟。。
Spring Native构建时间长怎么办?
不过,这确实提出了一个重要的观点。这些长的构建时间意味着它不适合像IDE中那样快速的开发周期,比如代码、编译、测试。没有人愿意在每个开发周期中多等几分钟。话虽如此,我们该怎么办?
在开发过程中,您仍然希望继续使用springjvm设置来保持那些快速的开发周期,所以不要改变这一点……一切照旧。但是,将改变的是如何在CI/CD管道中处理应用程序。我们能够吸收CI/CD环境中较高的构建时间,以换取快速部署和较低的资源使用率。非常适合!
Spring Native 运行Docker映像
在构建的最后,你需要列出你的docker映像,所以你需要有docker–install docker
。
docker images
这就是我看到的。。。
$ docker images | grep petclinic
petclinic-jdbc 0.0.1-SNAPSHOT e302b08389f0 41 years ago 143MB
我们可以看到镜像是142MB。让我们用下面的docker命令运行Pet Clinic Spring native应用程序。
docker run --name spring-native -p 8081:8080 --rm petclinic-jdbc:0.0.1-SNAPSHOT
这是我的控制台输出。看看第32行的启动时间…0.139秒,也就是139毫秒…哇,太不可思议了!
$ docker run --name spring-native -p 8081:8080 --rm petclinic-jdbc:0.0.1-SNAPSHOT
2021-04-15 19:12:58.918 INFO 1 --- [ main] o.s.nativex.NativeListener : This application is bootstrapped with code generated with Spring AOT
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.4)
2021-04-15 19:12:58.920 INFO 1 --- [ main] o.s.s.petclinic.PetClinicApplication : Starting PetClinicApplication using Java 1.8.0_282 on bb364a89b932 with PID 1 (/workspace/org.springframework.samples.petclinic.PetClinicApplication started by cnb in /workspace)
2021-04-15 19:12:58.920 INFO 1 --- [ main] o.s.s.petclinic.PetClinicApplication : No active profile set, falling back to default profiles: default
2021-04-15 19:12:58.987 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
Apr 15, 2021 7:12:58 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-8080"]
Apr 15, 2021 7:12:58 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service [Tomcat]
Apr 15, 2021 7:12:58 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet engine: [Apache Tomcat/9.0.44]
2021-04-15 19:12:58.989 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 68 ms
Apr 15, 2021 7:12:58 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring embedded WebApplicationContext
2021-04-15 19:12:59.022 INFO 1 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-04-15 19:12:59.034 WARN 1 --- [ main] ion$DefaultTemplateResolverConfiguration : Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)
Apr 15, 2021 7:12:59 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
2021-04-15 19:12:59.044 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-04-15 19:12:59.048 INFO 1 --- [ main] o.s.s.petclinic.PetClinicApplic
我们可以使用“docker stats
”命令查看资源占用情况,该命令显示57.17 MB内存和0.04%CPU。。
$ docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
37b55e79208a spring-native 0.04% 57.17MiB / 14.47GiB 0.39% 13.9kB / 606kB 2.85MB / 0B 18
比较Spring Native和Spring JVM | Pet Clinic
那么,这些数字与通常运行的基于spring jvm的pet clinic应用程序相比如何呢?我将克隆下面的git项目,并像以前一样在容器映像中执行完整构建。
$ git clone https://github.com/spring-projects/spring-petclinic.git
$ mvn clean package spring-boot:build-image
[INFO] Scanning for projects...
[INFO]
[INFO] ------------< org.springframework.samples:spring-petclinic >------------
[INFO] Building petclinic 2.4.2
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ spring-petclinic ---
[INFO] Deleting /home/andy/IdeaProjects/spring-petclinic/target
... omitted for brevity
[INFO] [creator] Saving docker.io/library/spring-petclinic:2.4.2...
[INFO] [creator] *** Images (9febd6ff3b0c):
[INFO] [creator] docker.io/library/spring-petclinic:2.4.2
[INFO]
[INFO] Successfully built image 'docker.io/library/spring-petclinic:2.4.2'
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:03 min
[INFO] Finished at: 2021-04-15T19:03:08Z
[INFO] ------------------------------------------------------------------------
上面的构建、运行大约40个测试并将jar文件打包到一个容器映像中,如上图所示,这个容器映像需要1:03分钟。如果您运行一个“mvn package
”,那么它将花费更少的时间,请注意在构建容器映像时会有开销。显然,基于Spring-JVM的项目在构建时比Spring-native版本有很大的优势。
下一个问题是,Spring JVM pet clinic应用程序的运行速度有多快?我们可以在上面的控制台输出中看到,springbootmaven插件创建了“spring-petclinic:2.4.2“码头工人形象。我们开始吧。
$ docker run --name spring-jvm -p 8080:8080 --rm spring-petclinic:2.4.2
Setting Active Processor Count to 4
Calculating JVM memory based on 10313200K available memory
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx9681983K -XX:MaxMetaspaceSize=119216K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 10313200K, Thread Count: 250, Loaded Class Count: 18634, Headroom: 0%)
Adding 129 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx9681983K -XX:MaxMetaspaceSize=119216K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true
|\ _,,,--,,_
/,`.-'`' ._ \-;;,_
_______ __|,4- ) )_ .;.(__`'-'__ ___ __ _ ___ _______
| | '---''(_/._)-'(_\_) | | | | | | | | |
| _ | ___|_ _| | | | | |_| | | | __ _ _
| |_| | |___ | | | | | | | | | | \ \ \ \
| ___| ___| | | | _| |___| | _ | | _| \ \ \ \
| | | |___ | | | |_| | | | | | | |_ ) ) ) )
|___| |_______| |___| |_______|_______|___|_| |__|___|_______| / / / /
==================================================================/_/_/_/
:: Built with Spring Boot :: 2.4.2
2021-04-15 19:12:32.367 INFO 1 --- [ main] o.s.s.petclinic.PetClinicApplication : Starting PetClinicApplication v2.4.2 using Java 1.8.0_282 on db37a7ce2b9e with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
2021-04-15 19:12:32.373 INFO 1 --- [ main] o.s.s.petclinic.PetClinicApplication : No active profile set, falling back to default profiles: default
2021-04-15 19:12:33.786 INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-04-15 19:12:33.869 INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 70 ms. Found 4 JPA repository interfaces.
2021-04-15 19:12:34.661 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-04-15 19:12:34.678 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-04-15 19:12:34.678 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-04-15 19:12:34.762 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-04-15 19:12:34.762 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2303 ms
2021-04-15 19:12:35.370 INFO 1 --- [ main] org.ehcache.core.EhcacheManager : Cache 'vets' created in EhcacheManager.
2021-04-15 19:12:35.383 INFO 1 --- [ main] org.ehcache.jsr107.Eh107CacheManager : Registering Ehcache MBean javax.cache:type=CacheStatistics,CacheManager=urn.X-ehcache.jsr107-default-config,Cache=vets
2021-04-15 19:12:35.391 INFO 1 --- [ main] org.ehcache.jsr107.Eh107CacheManager : Registering Ehcache MBean javax.cache:type=CacheStatistics,CacheManager=urn.X-ehcache.jsr107-default-config,Cache=vets
2021-04-15 19:12:35.468 INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-04-15 19:12:35.693 INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2021-04-15 19:12:35.891 INFO 1 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-04-15 19:12:35.954 INFO 1 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.27.Final
2021-04-15 19:12:36.122 INFO 1 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2021-04-15 19:12:36.279 INFO 1 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2021-04-15 19:12:37.274 INFO 1 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-04-15 19:12:37.284 INFO 1 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-04-15 19:12:38.354 INFO 1 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-04-15 19:12:39.692 INFO 1 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 13 endpoint(s) beneath base path '/actuator'
2021-04-15 19:12:39.781 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-04-15 19:12:39.813 INFO 1 --- [ main] o.s.s.petclinic.PetClinicApplication : Started PetClinicApplication in 7.898 seconds (JVM running for 8.397)
^C2021-04-15 19:12:52.336 INFO 1 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2021-04-15 19:12:52.341 INFO 1 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-04-15 19:12:52.347 INFO 1 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2021-04-15 19:12:52.378 INFO 1 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2021-04-15 19:12:52.383 INFO 1 --- [extShutdownHook] org.ehcache.core.EhcacheManager : Cache 'vets' removed from EhcacheManager.
运行基于JVM的应用程序,需要7.898秒来部署。这比基于Spring-native的应用程序慢得多,但我们预料到了这一点。
Spring-JVM与Spring-Native小结
我们可以通过“docker images
”看到下面列出的Spring-Native和基于Spring JVM的容器图像。它们的图像大小差别很大。Spring-Native显然占用更少的磁盘空间(只有143MB而不是261MB),这将转换为更小的运行时容器。
当这两个容器并排运行时,我们可以通过“docker stats
”看到Spring-Native在资源消耗方面再次处于领先地位。内存使用量实际上少了一个数量级(57.17MiB而不是508.8MiB)。CPU利用率也有所降低,但在本示例中,这还不够大。
$ docker images | grep petclinic
spring-petclinic 2.4.2 8846bf593485 41 years ago 261MB
petclinic-jdbc 0.0.1-SNAPSHOT e302b08389f0 41 years ago 143MB
$ docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
b73dcb33498e spring-jvm 0.21% 508.8MiB / 14.47GiB 3.43% 8.66kB / 7.51kB 1.29MB / 315kB 33
37b55e79208a spring-native 0.04% 57.17MiB / 14.47GiB 0.39% 13.9kB / 606kB 2.85MB / 0B 18
Spring Native的缺点和权衡
我们可以看到,所有这些都是以更长的构建时间为代价的,然而,我之前已经提到了如何处理这个问题。
整个应用程序的静态分析在构建时执行,以便收集反射、资源和动态代理等方面的配置信息。这意味着有时您必须在代码中以“提示”的形式提供额外的配置(如注释或属性),以便帮助GraalVM将代码从JVM移植到二进制binary。
老实说,现在有点乱,Spring团队会在这方面做出改进。下面是一个简单的示例,它强调了这样一个事实:GraalVM只支持JDK代理,而不支持CGLIB代理,因此需要。。
@SpringBootapplication (proxyBeanMethods = false)
当然,我只需要创建一个名为@SpringBootNativeApplication
的新注释,以便抽象出我们可能想要设置的任何其他属性。我不想展示这些“提示”的例子,因为我预计这在未来会有很大的变化。
目前(beta版),并不是所有的Spring服务都是springnative的候选服务,这意味着现在并不是所有的Spring启动程序都受支持。这显然会改变,但现在,这里是支持的Spring启动列表:https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/#support-spring-boot
Spring Native 总结
SpringNative现在只是测试版,但它已经发布了非常令人印象深刻的数字。看来在Spring的下一个主要版本中,我们最终将不得不在Spring JVM和SpringNative应用程序之间做出选择。
SpringNative目前是一个实验性的特性,但它将继续存在,并将得到飞跃性的改进。还记得Docker是如何作为Ubuntu的开发工具起步的吗?你今天去任何地方都不能不谈集装箱。这和Spring Native是一样的。
别搞错了,这是一个游戏规则改变者。通过SpringNative,Spring有机会成为这个要求不断提高的容器化世界中小型、快速和优化运行的应用程序的首选。
原文地址:http://mvpjava.com/spring-native/
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/1951.html
暂无评论