实现 0.059s 启动一个SpringBoot项目Spring

  • 77

实现 0.059s 启动一个SpringBoot项目

前言

最近自己用Spring Cloud Alibaba做了一个微服务架构的项目,部署的时候遇到了难题:内存不够。目前该项目有7个微服务,因为我只有一台阿里云的服务器(2C 4G),所以我只能把所有的微服务部署在一台服务器上,部署方式是使用docker制作springboot的fat jar镜像,每个微服务在不加任何JVM调优参数的情况下所占内存约500M。

由于是微服务所以肯定还要部署:nacos,除此之外还用到了redis、sentinel、rocketmq、elk等(mysql买的阿里云的),光是运行这些应用就占用内存2个多G,剩下的1个多G内存在部署4个微服务后就满了,于是开始对springboot应用的内存进行初步优化:

添加JVM参数优化内存大小

# JVM初始分配的内存由-Xms指定,默认是物理内存的1/64
-Xms128m
# JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4
-Xmx128m
# 规定了每个线程虚拟机栈及堆栈的大小,一般情况下,256k是足够的,此配置将会影响此进程中并发线程数的大小。
-Xss256k
# 指定并行GC线程的数量,一般最好和CPU核心数量相当
-XX:ParallelGCThreads=2

默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。

因此服务器一般设置-Xms-Xmx相等以避免在每次GC 后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。

默认情况下,当 CPU 数量小于8, ParallelGCThreads 的值等于 CPU 数量,我的服务器是2C的所以这个参数可省略。配置完成后,启动服务发现内存确实变小了,由原来的500M降至100~200M,但不是我想要的效果,我期望的效果是达到几十M的级别。

经网上查阅大量资料得知可以使用Spring Native这门新技术来实现我的需求。(该技术正处于快速迭代阶段,变动较大,建议用于个人学习,不要用于生产)

SpringBoot项目使用Spring Native后:

  1. 应用启动速度特别快,毫秒级别
  2. 运行时更低的内存消耗,官方展示的含有Spring Boot, Spring MVC, Jackson, Tomcat的镜像大小是50M
  3. 为了达到前面的效果,代价是构建时间更长(即使是一个Hello Word构建也需要2分钟,不过主要取决于电脑配置,我的是2min左右)

Spring Native是什么

简而言之就是为了提高Java在云原生的竞争力(个人理解)。

以下内容摘抄自GitHub上Spring Native的自述文件:

Spring Native 为使用GraalVM 原生映像编译器将 Spring 应用程序编译为原生可执行文件提供 beta 支持,以提供通常设计为打包在轻量级容器中的原生部署选项。实际上,目标是在这个新平台上支持几乎未修改的 Spring Boot 应用程序。

以下内容摘抄自其他博客:

近几年“原生”一词一直泛滥在云计算、边缘计算等领域中,而原生宠幸的语言也一直都是Golang,Rust等脱离Sandbox运行的开发语言。Java得益于上世纪流行的一次编译,到处执行的理念,流行至今,但也因为这个原因,导致Java程序脱离不了JVM运行环境,使得不那么受原生程序的青睐。在云原生泛滥的今天,臃肿的JVM使Java应用程序对比其他语言显得无比的庞大,各路大神也想了很多方式让Java变的更“原生”。

实现 0.059s 启动一个SpringBoot项目

实战

本次实战相关的环境信息如下:

  • 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

实现 0.059s 启动一个SpringBoot项目

从官方文档得知(上图)

使用 Spring Native 的应用程序应该使用 Java 11 或 Java 17 编译。

构建 Spring Boot 原生应用程序有两种主要方法:

  1. 使用Spring Boot Buildpacks 支持生成包含本机可执行文件的轻量级容器。
  2. 使用GraalVM 原生镜像 Maven 插件支持生成原生可执行文件。

经过各种踩坑后在本机上成功的使用了方法1和方法2。简单来说:

方法1就是在SpringBoot2.3后,可以使用spring-boot-maven-plugin插件来构建docker镜像,使用mvn spring-boot:build-image命令结合Docker的API来实现Spring Boot 原生应用程序的构建,成功执行后会直接生成一个docker镜像,然后run这个镜像就可以了,不用我们再写Dockerfile了,相关的参数配置都在pom.xml中配置(该插件的configuration标签下,和fabric8spotifydocker-maven-plugin很相似)。

方法2不需要安装docker,但要安装Visual Studio,然后执行mvn -Pnative package命令后会生成一个可执行文件(.exe),运行即可。

主要区别如下

1 环境依赖不同
  • 方法1需要安装Docker
  • 方法2需要安装Visual Studio(需要用到部分单个组件:2个MSVC,1个Windows 10 SDK)
2 执行的maven命令不同
  • 方法1是mvn spring-boot:build-image
  • 方法2是mvn -Pnative package

因为每个微服务使用Docker部署而不是exe文件,所以方法1正好符合我的需求,所以后文使用Spring Boot Buildpacks的方式构建Spring Boot原生应用程序。

1 安装Graal VM(graalvm-ce-java11-windows-amd64)

官方下载地址:

https://www.graalvm.org/downloads/

实现 0.059s 启动一个SpringBoot项目

实现 0.059s 启动一个SpringBoot项目

2 配置环境变量

实现 0.059s 启动一个SpringBoot项目

实现 0.059s 启动一个SpringBoot项目

实现 0.059s 启动一个SpringBoot项目

针对方法1的话,上面三张图好像只用配置JAVA_HOME就行,想一次成功的话建议3个都配,后续可以自行测试。

检验是否安装成功

实现 0.059s 启动一个SpringBoot项目

3 安装native-image

打开新的cmd,输入以下命令,等待安装

gu install native-image

这一步我执行失败了,解决方法就是从github上手动下载native-image,然后解压、安装

https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.3.0/native-image-installable-svm-java11-windows-amd64-21.3.0.jar

jar用WinRAR也是可以解压的,解压后如下

实现 0.059s 启动一个SpringBoot项目

在bin目录下打开cmd,输入以下命令,等待安装

$ gu install -L native-image*

4 安装 Desktop for Windows

具体步骤略,按照官方文档操作即可:

https://docs.docker.com/desktop/windows/install/

5 配置pom.xml

前面都是准备工作,这一步开始才是重点

首先快速创建一个Spring Boot项目,我命名为spring-native

完整的pom如下


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.0modelVersion>
    parent>
        groupId>org.springframework.bootgroupId>
        artifactId>spring-boot-starter-parentartifactId>
        version>2.6.2version>
        relativePath/> 
    parent>
    groupId>ltd.pcddgroupId>
    artifactId>spring-nativeartifactId>
    version>0.0.1-SNAPSHOTversion>
    name>spring-nativename>
    description>spring-nativedescription>
    properties>
        java.version>11java.version>
        repackage.classifier/>
        spring-native.version>0.11.1spring-native.version>
    properties>

    dependencies>
        dependency>
            groupId>org.springframework.bootgroupId>
            artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        dependency>
            groupId>org.springframework.experimentalgroupId>
            artifactId>spring-nativeartifactId>
            version>${spring-native.version}version>
        dependency>
    dependencies>

    build>
        plugins>
            plugin>
                groupId>org.springframework.experimentalgroupId>
                artifactId>spring-aot-maven-pluginartifactId>
                version>0.11.1version>
                executions>
                    execution>
                        id>generateid>
                        goals>
                            goal>generategoal>
                        goals>
                    execution>
                executions>
            plugin>

            
            plugin>
                groupId>org.springframework.bootgroupId>
                artifactId>spring-boot-maven-pluginartifactId>
                configuration>
                    image>
                        builder>paketobuildpacks/builder:tinybuilder>
                        env>
                            BP_NATIVE_IMAGE>trueBP_NATIVE_IMAGE>
                        env>
                    image>
                configuration>
            plugin>
        plugins>
    build>

    repositories>
        repository>
            id>spring-releaseid>
            name>Spring releasename>
            url>https://repo.spring.io/releaseurl>
        repository>
    repositories>

    pluginRepositories>
        pluginRepository>
            id>spring-releaseid>
            name>Spring releasename>
            url>https://repo.spring.io/releaseurl>
        pluginRepository>
    pluginRepositories>

project>

本文介绍的是Spring Native0.11.1版本,其对应的Spring Boot版本必须是2.6.2,以上只是一个最基本的配置案例,实际开发中还需要在spring-boot-maven-plugin插件的configuration标签下配置其他许许多多的参数。

例如docker远程的地址和证书的路径、jvm调优参数、配置文件指定、docker镜像名端口仓库地址等等,最好的方法就是看spring-boot-maven-plugin的官方文档,这里以配置jvm参数为例

实现 0.059s 启动一个SpringBoot项目

通过官方文档得知只需要在configuration标签下配置即可,例如

image>
 builder>paketobuildpacks/builder:tinybuilder>
 env>
  BP_NATIVE_IMAGE>trueBP_NATIVE_IMAGE>
  BPE_DELIM_JAVA_TOOL_OPTIONS xml:space="preserve"BPE_DELIM_JAVA_TOOL_OPTIONS>
  BPE_APPEND_JAVA_TOOL_OPTIONS>-Xms128mBPE_APPEND_JAVA_TOOL_OPTIONS>
  BPE_APPEND_JAVA_TOOL_OPTIONS>-Xmx128mBPE_APPEND_JAVA_TOOL_OPTIONS>
  BPE_APPEND_JAVA_TOOL_OPTIONS>-Xss256kBPE_APPEND_JAVA_TOOL_OPTIONS>
  BPE_APPEND_JAVA_TOOL_OPTIONS>-XX:ParallelGCThreads=2BPE_APPEND_JAVA_TOOL_OPTIONS>
  BPE_APPEND_JAVA_TOOL_OPTIONS>-XX:+PrintGCDetailsBPE_APPEND_JAVA_TOOL_OPTIONS>
 env>
image>

其他的配置参数还有很多。

官方文档:

https://docs.spring.io/spring-boot/docs/2.6.2/maven-plugin/reference/htmlsingle/#build-image

6 执行maven命令

mvn clean
mvn '-Dmaven.test.skip=true' spring-boot:build-image

下载完相关依赖后,电脑风扇就开始呼呼的转,查看任务管理器发现CPU利用率100%,内存使用量飙升,最后稳定在90%+。

构建成功

实现 0.059s 启动一个SpringBoot项目

7 创建并运行容器

查看所有镜像

实现 0.059s 启动一个SpringBoot项目

spring-native就是构建的镜像

创建并运行容器

实现 0.059s 启动一个SpringBoot项目

Docker Desktop查看日志,发现应用成功启动,启动仅耗时。,也就是59ms,果然印证了Spring Native启动是毫秒级别这句话。

实现 0.059s 启动一个SpringBoot项目

成功调用接口

实现 0.059s 启动一个SpringBoot项目

Docker Desktop查看占用内存,仅28M左右。

实现 0.059s 启动一个SpringBoot项目

不使用Spring Native启动应用

实现 0.059s 启动一个SpringBoot项目

实现 0.059s 启动一个SpringBoot项目

启动耗时3s,占用内存高达511M,高下立判。

文章仅供参考,建议结合Spring Native官方最新文档学习。

📚文章说明:
1、本文章链接失效后,请通过右下角企鹅QQ告知管理员,24小时内补链接,谢谢。点击下载说明了解。
2、天天精品分享的资源均通过网络公开合法渠道获取的,仅阅读交流测试使用,请在下载后24小时内删除。
3、版权归作者或出版社方所有,本站不对涉及的版权问题负法律责任。点击免责申明了解详情。
4、若版权方认为天天精品侵权,请联系客服QQ或发送邮件myttjp@163.com处理。
5、会员和精品豆系对搜集搬运、整理及网站运营做的友情赞助,非购买文件费用,敬请谅解。点击关于本站了解本站。
6、每位访客应尊重版权方的知识产权,支持版权方和出版社。

发表评论