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内容如下,有几处要注意的地方稍后提到:
4.0.0 webmvc org.springframework.boot spring-boot-starter-parent 2.5.0-SNAPSHOT com.bolingcavalry spring-native-tutorials 1.0-SNAPSHOT pom 1.8 dmikusa/graalvm-tiny 2020.0.2 spring-release Springrelease https://repo.spring.io/release false spring-milestone Springmilestone https://repo.spring.io/milestone false spring-snapshot SpringSnapshots https://repo.spring.io/snapshot false spring-release Springrelease https://repo.spring.io/release false spring-milestone Springmilestone https://repo.spring.io/milestone false spring-snapshot SpringSnapshots https://repo.spring.io/snapshot false org.springframework.experimental spring-native 0.10.0-SNAPSHOT org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin ${classifier} ${builder} true ${native.build.args} IF_NOT_PRESENT org.springframework.experimental spring-aot-maven-plugin 0.10.0-SNAPSHOT test-generate test-generate generate generate
上述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,一个负责构建镜像:
spring-native-tutorials com.bolingcavalry 1.0-SNAPSHOT 4.0.0 webmvc org.springframework.experimental spring-native org.springframework.boot spring-boot-starter-web org.apache.tomcat.embed tomcat-embed-core org.apache.tomcat.embed tomcat-embed-websocket org.apache.tomcat.experimental tomcat-embed-programmatic ${tomcat.version} org.springframework.boot spring-boot-starter-test test org.springframework.experimental spring-aot-maven-plugin true org.springframework.boot spring-boot-maven-plugin
代码很简单,一个普通的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 b8ff54813ae041yearsago69B41yearsago452kB 41yearsago2.51MB 41yearsago57.2MB 41yearsago1.4MB 41yearsago268B 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依赖注入方式和区别有哪些
小编给大家分享一下springDI依赖注入方式和区别有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家...
-
spring data jpa开启批量插入、批量更新的示例分析
这篇文章给大家分享的是有关springdatajpa开启批量插入、批量更新的示例分析的内容。小编觉得挺实用的,因此分享给大家做...
-
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,如何保证微服务内安全,恰卡网带你了解更多相关信息。一、简介在微服务的架构下,我们需要把系统的业...
-
Spring(Cloud,Config,使用本地配置文件方式)
Spring,Cloud,Config,使用本地配置文件方式,恰卡网带你了解更多相关信息。一、简介在分布式系统中,由于服...