Spring Native项目的示例分析
小编给大家分享一下Spring Native项目的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
关于Spring Native
Spring官方博客于2021年03月11日宣布Spring Native的beta版本发布,借助Spring Native可以将spring应用与GraalVM集成到native image中;
native image是GraalVM的一项技术,会将java应用的字节码编译成可执行文件,还会与JDK的本地库做静态链接,运行应用时无需Java虚拟机,自身已集成了内存管理,线程调度等能力,更多信息请参考:https://www.graalvm.org/reference-manual/native-image/
本文以实战为主,因此不会用太多篇幅介绍Spring Native的理论和优势,这里简单小结几个重要特性:
应用启动速度不超过100毫秒;
启动即达到性能峰值(C1、C2等手段已经用不上了)
运行时更低的内存消耗;
docker镜像不含JDK(所需文件已经抽取出来放入镜像),官方展示的含有Spring Boot, Spring MVC, Jackson, Tomcat的镜像大小是50M;
为了达到前面的效果,代价是构建时间更长;
Spring Native到底是什么
个人的理解:Spring Native是Spring提供的、制作native image的技术方案,涉及到以下关键技术:
Spring ahead-of-time (AOT) 插件,对spring应用做AOT处理,使得传统虚拟机的class lazy loading在不复存在;
spring-boot-maven-plugin插件在构建docker镜像的时候,使用了名为dmikusa/graalvm-tiny的镜像作为构建工具,这个工具负责将当前工程的构建结果和GraalVM集成在一起,最终制作成native image;
本篇概览
作为实战风格的文章,本篇主要内容是开发springboot应用再构建为native image,然后验证其功能和效果,本文由以下内容构成:
环境信息
新建名为spring-native-tutorials的maven父工程,对实战用到的依赖库、插件等做统一配置;
新建名为webmvc的maven子工程,这是个springboot应用;
将webmvc构建为native image,这是个docker镜像;
在docker中启动镜像,验证是否可用,并检查相关相关指标;
环境信息
本次实战相关的环境信息如下:
电脑:MacBook pro 13寸 2018
操作系统:macOS Big Sur 11.2.3
IDE:IntelliJ IDEA 2018.3.5 (Ultimate Edition)
docker:20.10.5
JDK:1.8.0_211
maven:3.6.0
springboot:2.5.0-SNAPSHOT
spring-aot-maven-plugin:0.10.0-SNAPSHOT
源码下载 本篇实战中的完整源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
名称 | 链接 | 备注 |
---|---|---|
项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
这个git项目中有多个文件夹,本次实战的源码在spring-native-tutorials文件夹下,如下图红框所示:
新建名为spring-native-tutorials的maven父工程
对Spring Native的学习不是写出helloworld就完事,因此这里先创建一个父工程,为今后所有的应用提供统一的依赖库、插件管理;
新建名为spring-native-tutorials的maven父工程,pom.xml内容如下,有几处要注意的地方稍后提到:
<?xmlversion="1.0"encoding="UTF-8"?> <projectxmlns="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.0http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <modules> <module>webmvc</module> </modules> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0-SNAPSHOT</version> <relativePath/> </parent> <groupId>com.bolingcavalry</groupId> <artifactId>spring-native-tutorials</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <properties> <java.version>1.8</java.version> <!--springboot生成jar文件的文件名后缀,用来避免SpringBootrepackaging和native-image-maven-plugin插件之间可能存在的冲突--> <classifier/> <!--构建镜像时的定制参数--> <native.build.args/> <!--指定使用dmikusa/graalvm-tiny这个镜像作为构建工具,来构建镜像--> <builder>dmikusa/graalvm-tiny</builder> <!--springcloud版本--> <spring-cloud.version>2020.0.2</spring-cloud.version> </properties> <!--插件管理--> <pluginRepositories> <pluginRepository> <id>spring-release</id> <name>Springrelease</name> <url>https://repo.spring.io/release</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-milestone</id> <name>Springmilestone</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-snapshot</id> <name>SpringSnapshots</name> <url>https://repo.spring.io/snapshot</url> <releases> <enabled>false</enabled> </releases> </pluginRepository> </pluginRepositories> <!--仓库管理--> <repositories> <repository> <id>spring-release</id> <name>Springrelease</name> <url>https://repo.spring.io/release</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>spring-milestone</id> <name>Springmilestone</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>spring-snapshot</id> <name>SpringSnapshots</name> <url>https://repo.spring.io/snapshot</url> <releases> <enabled>false</enabled> </releases> </repository> </repositories> <!--依赖包版本管理--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-native</artifactId> <version>0.10.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!--插件配置--> <build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <classifier>${classifier}</classifier> <image> <builder>${builder}</builder> <env> <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE> <BP_NATIVE_IMAGE_BUILD_ARGUMENTS>${native.build.args}</BP_NATIVE_IMAGE_BUILD_ARGUMENTS> </env> <!--执行构建任务的镜像,如果在当前环境不存在才会远程下载--> <pullPolicy>IF_NOT_PRESENT</pullPolicy> </image> </configuration> </plugin> <!--aot插件,ahead-of-timetransformations--> <plugin> <groupId>org.springframework.experimental</groupId> <artifactId>spring-aot-maven-plugin</artifactId> <version>0.10.0-SNAPSHOT</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> </pluginManagement> </build> </project>
上述pom.xml有以下几处需要注意:
插件仓库、依赖库仓库、依赖库版本的配置都集中在这里;
配置好spring-aot-maven-plugin和spring-boot-maven-plugin这两个插件,子工程会用到;
spring-boot-maven-plugin插件制作docker镜像的时候,又会用到dmikusa/graalvm-tiny镜像,这才是真正构建native image的工具;
新建springboot类型的maven子工程
新建名为webmvc的子工程,pom.xml内容如下,可见内容很简单,就是常规依赖库和父工程配置的两个插件,一个负责执行AOT,一个负责构建镜像:
<?xmlversion="1.0"encoding="UTF-8"?> <projectxmlns="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.0http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring-native-tutorials</artifactId> <groupId>com.bolingcavalry</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>webmvc</artifactId> <dependencies> <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-native</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> </exclusion> <exclusion> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.tomcat.experimental</groupId> <artifactId>tomcat-embed-programmatic</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.experimental</groupId> <artifactId>spring-aot-maven-plugin</artifactId> <configuration> <removeSpelSupport>true</removeSpelSupport> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
代码很简单,一个普通的springboot应用,带http接口:
packagecom.bolingcavalry.webmvc; importorg.springframework.boot.SpringApplication; importorg.springframework.boot.autoconfigure.SpringBootApplication; importorg.springframework.http.HttpStatus; importorg.springframework.web.bind.annotation.GetMapping; importorg.springframework.web.bind.annotation.ResponseStatus; importorg.springframework.web.bind.annotation.RestController; importjava.time.LocalDateTime; @SpringBootApplication @RestController publicclassWebmvcApplication{ publicstaticvoidmain(String[]args){ SpringApplication.run(WebmvcApplication.class,args); } @ResponseStatus(HttpStatus.ACCEPTED) @GetMapping("/status") publicStringstatus(){ return"status"; } @GetMapping("/") publicStringhello(){ return"1.HellofromSpringMVCandTomcat,"+LocalDateTime.now(); } }
现在编码已完成,来构建docker镜像吧,进入父工程的pom.xml所在目录,执行以下命令:
mvnclean-U-DskipTestsspring-boot:build-image
构建成功后输出信息如下(篇幅所限仅截取最后一小段),耗时4分25秒,期间笔记本风扇狂转:
..[INFO] Successfully built image 'docker.io/library/webmvc:1.0-SNAPSHOT'[INFO] [INFO] ------------------------------------------------------------------------[INFO] Reactor Summary for spring-native-tutorials 1.0-SNAPSHOT:[INFO] [INFO] spring-native-tutorials ............................ SUCCESS [ 1.786 s][INFO] webmvc ............................................. SUCCESS [04:19 min][INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time: 04:25 min[INFO] Finished at: 2021-05-22T16:36:44+08:00[INFO] ------------------------------------------------------------------------[WARNING] The requested profile "nexus" could not be activated because it does not exist.
执行docker images命令,如下图,可见镜像已经生成:
查看镜像构成,可见每个layer都不大,共计七十多M:
(base)zhaoqindeMBP:~zhaoqin$dockerhistorywebmvc:1.0-SNAPSHOT IMAGECREATEDCREATEDBYSIZECOMMENT b8ff54813ae041yearsago69B <missing>41yearsago452kB <missing>41yearsago2.51MB <missing>41yearsago57.2MB <missing>41yearsago1.4MB <missing>41yearsago268B <missing>41yearsago17.3MB
镜像构建成功,可以验证基本功能了;
验证
执行以下命令,创建一个临时容器(控制台结束后容器会被清理掉):
dockerrun--rm-p8080:8080webmvc:1.0-SNAPSHOT
控制台输出如下,79毫秒启动完成,真是一眨间的功夫:
(base)zhaoqindeMBP:~zhaoqin$dockerrun--rm-p8080:8080webmvc:1.0-SNAPSHOT 2021-05-2209:34:57.578INFO1---[main]o.s.nativex.NativeListener:ThisapplicationisbootstrappedwithcodegeneratedwithSpringAOT ._________ /\\/___'_____(_)______\\\\ (()\___|'_|'_||'_\/_`|\\\\ \\/___)||_)|||||||(_||)))) '|____|.__|_||_|_||_\__,|//// =========|_|==============|___/=/_/_/_/ ::SpringBoot::(v2.5.0-SNAPSHOT) 2021-05-2209:34:57.586INFO1---[main]c.b.webmvc.WebmvcApplication:StartingWebmvcApplicationusingJava1.8.0_292on3529ec458896withPID1(/workspace/com.bolingcavalry.webmvc.WebmvcApplicationstartedbycnbin/workspace) 2021-05-2209:34:57.586INFO1---[main]c.b.webmvc.WebmvcApplication:Noactiveprofileset,fallingbacktodefaultprofiles:default 2021-05-2209:34:57.661INFO1---[main]o.s.b.w.embedded.tomcat.TomcatWebServer:Tomcatinitializedwithport(s):8080(http) May22,20219:34:57AMorg.apache.coyote.AbstractProtocolinit INFO:InitializingProtocolHandler["http-nio-8080"] May22,20219:34:57AMorg.apache.catalina.core.StandardServicestartInternal INFO:Startingservice[Tomcat] May22,20219:34:57AMorg.apache.catalina.core.StandardEnginestartInternal INFO:StartingServletengine:[ApacheTomcat/9.0.46] May22,20219:34:57AMorg.apache.catalina.core.ApplicationContextlog INFO:InitializingSpringembeddedWebApplicationContext 2021-05-2209:34:57.669INFO1---[main]w.s.c.ServletWebServerApplicationContext:RootWebApplicationContext:initializationcompletedin79ms May22,20219:34:57AMorg.apache.coyote.AbstractProtocolstart INFO:StartingProtocolHandler["http-nio-8080"] 2021-05-2209:34:57.713INFO1---[main]o.s.b.w.embedded.tomcat.TomcatWebServer:Tomcatstartedonport(s):8080(http)withcontextpath'' 2021-05-2209:34:57.713INFO1---[main]c.b.webmvc.WebmvcApplication:StartedWebmvcApplicationin0.178seconds(JVMrunningfor0.19) 2021-05-2209:34:57.713INFO1---[main]o.s.b.a.ApplicationAvailabilityBean:ApplicationavailabilitystateLivenessStatechangedtoCORRECT 2021-05-2209:34:57.714INFO1---[main]o.s.b.a.ApplicationAvailabilityBean:ApplicationavailabilitystateReadinessStatechangedtoACCEPTING_TRAFFIC
浏览器访问本机8080端口,如下图,应用基本功能正常:
再看看资源使用情况,命令是docker stats,如下可见,内存仅用了30M:
CONTAINERIDNAMECPU%MEMUSAGE/LIMITMEM%NETI/OBLOCKI/OPIDS 6ce6c66fb4dejovial_hertz0.11%30.69MiB/3.844GiB0.78%1.49kB/158B4.31MB/0B18
我曾经在hub.docker.com上放了一个传统springboot应用制作的镜像bolingcavalry/hellojib:0.0.1-SNAPSHOT,现在拿来和Spring Native镜像对比一下,启动信息如下,耗时2036毫秒:
(base)zhaoqindeMacBook-Pro:~zhaoqin$dockerrun--rm-Pdocker.io/bolingcavalry/hellojib:0.0.1-SNAPSHOT ._________ /\\/___'_____(_)______\\\\ (()\___|'_|'_||'_\/_`|\\\\ \\/___)||_)|||||||(_||)))) '|____|.__|_||_|_||_\__,|//// =========|_|==============|___/=/_/_/_/ ::SpringBoot::(v2.1.6.RELEASE) 2021-05-2211:13:28.121INFO1---[main]c.b.hellojib.HellojibApplication:StartingHellojibApplicationonffb32e5b68b9withPID1(/app/classesstartedbyrootin/) 2021-05-2211:13:28.128INFO1---[main]c.b.hellojib.HellojibApplication:Noactiveprofileset,fallingbacktodefaultprofiles:default 2021-05-2211:13:30.000INFO1---[main]o.s.b.w.embedded.tomcat.TomcatWebServer:Tomcatinitializedwithport(s):8080(http) 2021-05-2211:13:30.054INFO1---[main]o.apache.catalina.core.StandardService:Startingservice[Tomcat] 2021-05-2211:13:30.054INFO1---[main]org.apache.catalina.core.StandardEngine:StartingServletengine:[ApacheTomcat/9.0.21] 2021-05-2211:13:30.241INFO1---[main]o.a.c.c.C.[Tomcat].[localhost].[/]:InitializingSpringembeddedWebApplicationContext 2021-05-2211:13:30.241INFO1---[main]o.s.web.context.ContextLoader:RootWebApplicationContext:initializationcompletedin2036ms 2021-05-2211:13:30.715INFO1---[main]o.s.s.concurrent.ThreadPoolTaskExecutor:InitializingExecutorService'applicationTaskExecutor' 2021-05-2211:13:31.103INFO1---[main]o.s.b.w.embedded.tomcat.TomcatWebServer:Tomcatstartedonport(s):8080(http)withcontextpath'' 2021-05-2211:13:31.110INFO1---[main]c.b.hellojib.HellojibApplication:StartedHellojibApplicationin3.618seconds(JVMrunningfor4.297) 2021-05-2211:13:48.866INFO1---[nio-8080-exec-1]o.a.c.c.C.[Tomcat].[localhost].[/]:InitializingSpringDispatcherServlet'dispatcherServlet' 2021-05-2211:13:48.866INFO1---[nio-8080-exec-1]o.s.web.servlet.DispatcherServlet:InitializingServlet'dispatcherServlet' 2021-05-2211:13:48.880INFO1---[nio-8080-exec-1]o.s.web.servlet.DispatcherServlet:Completedinitializationin14ms
再用docker stats对比内存,传统springboot应用的容器消耗了三百多兆内存:
CONTAINERIDNAMECPU%MEMUSAGE/LIMITMEM%NETI/OBLOCKI/OPIDS ffb32e5b68b9eager_williamson0.64%356.3MiB/3.844GiB9.05%3.46kB/2.29kB0B/0B31 6ce6c66fb4dejovial_hertz0.11%30.69MiB/3.844GiB0.78%1.49kB/158B4.31MB/0B18
综上所述,Spring Native带来的优势是很明显的,不过请注意:2021年03月11日官方宣布的Spring Native只是beta版本,请不要用于生产环境!!!
下载插件失败
在实际操作过程中,经常会遇到maven插件或者docker镜像下载失败的情况,除了多试几次,您还可以考虑将项目放到github上去,借助github action在云端完成镜像构建,具体操作请参考《用GitHub Actions制作Docker镜像》
不用开发,直接体验
我已将镜像上传到hub.docker.com,完整名称是bolingcavalry/webmvc:1.0-SNAPSHOT,如果您只想体验一下native image的效果可以直接下载该镜像使用;
以上是“Spring Native项目的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注恰卡编程网行业资讯频道!
推荐阅读
-
Spring框架基于注解开发CRUD详解
-
spring DI依赖注入方式和区别有哪些
-
spring data jpa开启批量插入、批量更新的示例分析
-
spring中怎么利用FactoryBean配置Bean
这篇文章将为大家详细讲解有关spring中怎么利用FactoryBean配置Bean,文章内容质量较高,因此小编分享给大家做个参考...
-
如何解决解决Spring Boot正常启动后访问Controller提示404的问题
小编给大家分享一下如何解决解决SpringBoot正常启动后访问Controller提示404的问题,希望大家阅读完这篇文章之后...
-
Spring中怎么解决循环依赖问题
本篇文章给大家分享的是有关Spring中怎么解决循环依赖问题,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有...
-
Spring(aop,如何通过获取代理对象实现事务切换)
Spring,aop,如何通过获取代理对象实现事务切换,恰卡网带你了解更多相关信息。Springaop获取代理对象实现...
-
Spring(bean,四种注入方式详解)
Spring,bean,四种注入方式详解,恰卡网带你了解更多相关信息。目录一、Set方式注入pojo层:1.xml文件t...
-
Spring(Cloud,如何保证微服务内安全)
-
Spring(Cloud,Config,使用本地配置文件方式)