Spring Native實現(xiàn)0.059s啟動一個SpringBoot項目
前言
最近自己用Spring Cloud Alibaba做了一個微服務(wù)架構(gòu)的項目,部署的時候遇到了難題:內(nèi)存不夠。目前該項目有7個微服務(wù),因為我只有一臺阿里云的服務(wù)器(2C 4G),所以我只能把所有的微服務(wù)部署在一臺服務(wù)器上,部署方式是使用docker制作springboot的fat jar鏡像,每個微服務(wù)在不加任何JVM調(diào)優(yōu)參數(shù)的情況下所占內(nèi)存約500M。
由于是微服務(wù)所以肯定還要部署:nacos,除此之外還用到了redis、sentinel、rocketmq、elk等(mysql買的阿里云的),光是運(yùn)行這些應(yīng)用就占用內(nèi)存2個多G,剩下的1個多G內(nèi)存在部署4個微服務(wù)后就滿了,于是開始對springboot應(yīng)用的內(nèi)存進(jìn)行初步優(yōu)化:
添加JVM參數(shù)優(yōu)化內(nèi)存大小
# JVM初始分配的內(nèi)存由-Xms指定,默認(rèn)是物理內(nèi)存的1/64 -Xms128m # JVM最大分配的內(nèi)存由-Xmx指定,默認(rèn)是物理內(nèi)存的1/4 -Xmx128m # 規(guī)定了每個線程虛擬機(jī)棧及堆棧的大小,一般情況下,256k是足夠的,此配置將會影響此進(jìn)程中并發(fā)線程數(shù)的大小。 -Xss256k # 指定并行GC線程的數(shù)量,一般最好和CPU核心數(shù)量相當(dāng) -XX:ParallelGCThreads=2
默認(rèn)空余堆內(nèi)存小于40%時,JVM就會增大堆直到-Xmx的最大限制;空余堆內(nèi)存大于70%時,JVM會減少堆直到 -Xms的最小限制。
因此服務(wù)器一般設(shè)置-Xms、-Xmx相等以避免在每次GC 后調(diào)整堆的大小。對象的堆內(nèi)存由稱為垃圾回收器的自動內(nèi)存管理系統(tǒng)回收。
默認(rèn)情況下,當(dāng) CPU 數(shù)量小于8, ParallelGCThreads 的值等于 CPU 數(shù)量,我的服務(wù)器是2C的所以這個參數(shù)可省略。配置完成后,啟動服務(wù)發(fā)現(xiàn)內(nèi)存確實變小了,由原來的500M降至100~200M,但不是我想要的效果,我期望的效果是達(dá)到幾十M的級別。
經(jīng)網(wǎng)上查閱大量資料得知可以使用Spring Native這門新技術(shù)來實現(xiàn)我的需求。(該技術(shù)正處于快速迭代階段,變動較大,建議用于個人學(xué)習(xí),不要用于生產(chǎn))
SpringBoot項目使用Spring Native后:
- 應(yīng)用啟動速度特別快,毫秒級別
- 運(yùn)行時更低的內(nèi)存消耗,官方展示的含有Spring Boot, Spring MVC, Jackson, Tomcat的鏡像大小是50M
- 為了達(dá)到前面的效果,代價是構(gòu)建時間更長(即使是一個Hello Word構(gòu)建也需要2分鐘,不過主要取決于電腦配置,我的是2min左右)
Spring Native是什么
簡而言之就是為了提高Java在云原生的競爭力(個人理解)。
以下內(nèi)容摘抄自GitHub上Spring Native的自述文件:
Spring Native 為使用GraalVM 原生映像編譯器將 Spring 應(yīng)用程序編譯為原生可執(zhí)行文件提供 beta
支持,以提供通常設(shè)計為打包在輕量級容器中的原生部署選項。實際上,目標(biāo)是在這個新平臺上支持幾乎未修改的 Spring Boot 應(yīng)用程序。
以下內(nèi)容摘抄自其他博客:
近幾年“原生”一詞一直泛濫在云計算、邊緣計算等領(lǐng)域中,而原生寵幸的語言也一直都是Golang,Rust等脫離Sandbox運(yùn)行的開發(fā)語言。Java得益于上世紀(jì)流行的一次編譯,到處執(zhí)行的理念,流行至今,但也因為這個原因,導(dǎo)致Java程序脫離不了JVM運(yùn)行環(huán)境,使得不那么受原生程序的青睞。在云原生泛濫的今天,臃腫的JVM使Java應(yīng)用程序?qū)Ρ绕渌Z言顯得無比的龐大,各路大神也想了很多方式讓Java變的更“原生”。
實戰(zhàn)
本次實戰(zhàn)相關(guān)的環(huán)境信息如下:
- OS:Windows10 21H1
- IDE:IntelliJ IDEA 2021.2.3
- JDK:graalvm-ce-java11-21.3.0
- Maven:3.6.3
- Docker Desktop for Windows: 20.10.12
- Spring Boot:2.6.2
- Spring Native:0.11.1
從官方文檔得知(上圖)
使用 Spring Native 的應(yīng)用程序應(yīng)該使用 Java 11 或 Java 17 編譯。
構(gòu)建 Spring Boot 原生應(yīng)用程序有兩種主要方法:
使用Spring Boot Buildpacks 支持生成包含本機(jī)可執(zhí)行文件的輕量級容器。
使用GraalVM 原生鏡像 Maven 插件支持生成原生可執(zhí)行文件。
經(jīng)過各種踩坑后在本機(jī)上成功的使用了方法1和方法2。簡單來說:
方法1就是在SpringBoot2.3后,可以使用spring-boot-maven-plugin插件來構(gòu)建docker鏡像,使用mvn spring-boot:build-image命令結(jié)合Docker的API來實現(xiàn)Spring Boot 原生應(yīng)用程序的構(gòu)建,成功執(zhí)行后會直接生成一個docker鏡像,然后run這個鏡像就可以了,不用我們再寫Dockerfile了,相關(guān)的參數(shù)配置都在pom.xml中配置(該插件的configuration標(biāo)簽下,和fabric8或spotify的docker-maven-plugin很相似)。
方法2不需要安裝docker,但要安裝Visual Studio,然后執(zhí)行mvn -Pnative package命令后會生成一個可執(zhí)行文件(.exe),運(yùn)行即可。
主要區(qū)別如下
1 環(huán)境依賴不同
- 方法1需要安裝Docker
- 方法2需要安裝Visual Studio(需要用到部分單個組件:2個MSVC,1個Windows 10 SDK)
2 執(zhí)行的maven命令不同
- 方法1是mvn spring-boot:build-image
- 方法2是mvn -Pnative package
因為每個微服務(wù)使用Docker部署而不是exe文件,所以方法1正好符合我的需求,所以后文使用Spring Boot Buildpacks的方式構(gòu)建Spring Boot原生應(yīng)用程序。
安裝Graal VM(graalvm-ce-java11-windows-amd64)
官方下載地址:
https://www.graalvm.org/downloads/
配置環(huán)境變量
針對方法1的話,上面三張圖好像只用配置JAVA_HOME就行,想一次成功的話建議3個都配,后續(xù)可以自行測試。
檢驗是否安裝成功
安裝native-image
打開新的cmd,輸入以下命令,等待安裝
gu install native-image
這一步我執(zhí)行失敗了,解決方法就是從github上手動下載native-image,然后解壓、安裝
jar用WinRAR也是可以解壓的,解壓后如下
在bin目錄下打開cmd,輸入以下命令,等待安裝
$ gu install -L native-image*
安裝 Desktop for Windows
具體步驟略,按照官方文檔操作即可:
配置pom.xml
前面都是準(zhǔn)備工作,這一步開始才是重點(diǎn)
首先快速創(chuàng)建一個Spring Boot項目,我命名為spring-native
完整的pom如下
<?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.6.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>ltd.pcdd</groupId> <artifactId>spring-native</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-native</name> <description>spring-native</description> <properties> <java.version>11</java.version> <repackage.classifier/> <spring-native.version>0.11.1</spring-native.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-native</artifactId> <version>${spring-native.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.experimental</groupId> <artifactId>spring-aot-maven-plugin</artifactId> <version>0.11.1</version> <executions> <execution> <id>generate</id> <goals> <goal>generate</goal> </goals> </execution> </executions> </plugin> <!--Spring Boot 2.3發(fā)布后帶來了新特性之一就是對構(gòu)建鏡像的便捷支持--> <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> </plugins> </build> <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> </project>
本文介紹的是Spring Native0.11.1版本,其對應(yīng)的Spring Boot版本必須是2.6.2,以上只是一個最基本的配置案例,實際開發(fā)中還需要在spring-boot-maven-plugin插件的configuration標(biāo)簽下配置其他許許多多的參數(shù)。
例如docker遠(yuǎn)程的地址和證書的路徑、jvm調(diào)優(yōu)參數(shù)、配置文件指定、docker鏡像名端口倉庫地址等等,最好的方法就是看spring-boot-maven-plugin的官方文檔,這里以配置jvm參數(shù)為例
通過官方文檔得知只需要在configuration標(biāo)簽下配置即可,例如
<image> <builder>paketobuildpacks/builder:tiny</builder> <env> <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE> <BPE_DELIM_JAVA_TOOL_OPTIONS xml:space="preserve"> </BPE_DELIM_JAVA_TOOL_OPTIONS> <BPE_APPEND_JAVA_TOOL_OPTIONS>-Xms128m</BPE_APPEND_JAVA_TOOL_OPTIONS> <BPE_APPEND_JAVA_TOOL_OPTIONS>-Xmx128m</BPE_APPEND_JAVA_TOOL_OPTIONS> <BPE_APPEND_JAVA_TOOL_OPTIONS>-Xss256k</BPE_APPEND_JAVA_TOOL_OPTIONS> <BPE_APPEND_JAVA_TOOL_OPTIONS>-XX:ParallelGCThreads=2</BPE_APPEND_JAVA_TOOL_OPTIONS> <BPE_APPEND_JAVA_TOOL_OPTIONS>-XX:+PrintGCDetails</BPE_APPEND_JAVA_TOOL_OPTIONS> </env> </image>
其他的配置參數(shù)還有很多。
官方文檔:
https://docs.spring.io/spring-boot/docs/2.6.2/maven-plugin/reference/htmlsingle/#build-image
執(zhí)行maven命令
mvn clean mvn '-Dmaven.test.skip=true' spring-boot:build-image
下載完相關(guān)依賴后,電腦風(fēng)扇就開始呼呼的轉(zhuǎn),查看任務(wù)管理器發(fā)現(xiàn)CPU利用率100%,內(nèi)存使用量飆升,最后穩(wěn)定在90%+。
構(gòu)建成功
創(chuàng)建并運(yùn)行容器
查看所有鏡像
spring-native就是構(gòu)建的鏡像
創(chuàng)建并運(yùn)行容器
在Docker Desktop查看日志,發(fā)現(xiàn)應(yīng)用成功啟動,啟動僅耗時。,也就是59ms,果然印證了Spring Native啟動是毫秒級別這句話。
成功調(diào)用接口
在Docker Desktop查看占用內(nèi)存,僅28M左右。
不使用Spring Native啟動應(yīng)用
啟動耗時3s,占用內(nèi)存高達(dá)511M,高下立判。
文章僅供參考,建議結(jié)合Spring Native官方最新文檔學(xué)習(xí)。
https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/index.html
到此這篇關(guān)于Spring Native實現(xiàn)0.059s啟動一個SpringBoot項目的文章就介紹到這了,更多相關(guān)Spring Native啟動項目內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中JMM與volatile關(guān)鍵字的學(xué)習(xí)
這篇文章主要介紹了通過實例解析JMM和Volatile關(guān)鍵字的學(xué)習(xí),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2021-09-09Java實現(xiàn)Kafka生產(chǎn)者和消費(fèi)者的示例
這篇文章主要介紹了Java實現(xiàn)Kafka生產(chǎn)者和消費(fèi)者的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02Java基礎(chǔ)教程之理解Annotation詳細(xì)介紹
這篇文章主要介紹了Java基礎(chǔ)教程之理解Annotation詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-01-01淺析如何將多個SpringBoot項目打包到一個Docker容器中
在現(xiàn)代軟件開發(fā)中,Docker已成為一種流行的容器化技術(shù),能夠簡化應(yīng)用的部署和管理,本文將詳細(xì)介紹如何將多個Spring Boot項目打包到一個Docker容器中,希望對大家有所幫助2024-10-10安裝elasticsearch-analysis-ik中文分詞器的步驟講解
今天小編就為大家分享一篇關(guān)于安裝elasticsearch-analysis-ik中文分詞器的步驟講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02Java連接mysql數(shù)據(jù)庫的詳細(xì)教程(推薦)
這篇文章主要介紹了Java連接mysql數(shù)據(jù)庫的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08使用mybatis的typeHandler對clob進(jìn)行流讀寫方式
這篇文章主要介紹了使用mybatis的typeHandler對clob進(jìn)行流讀寫方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01