我是靠谱客的博主 懦弱枕头,最近开发中收集的这篇文章主要介绍这样做优化,实现 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变的更“原生”。

20f514dfbd2f1335d5ba97b71a53e616.png

实战

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

  • 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

7ae6d0a3f405988a9f69b1103f4754dd.png

从官方文档得知(上图)

使用 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)

  • 另外,推荐公众 号Java精选,回复java面试,获取面试资料,支持在线刷题。

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/

6198aba466a9fe5a559b3b8e78ca103e.png

3d5644ec02c176a5050dfaa256cb9fc2.png

2 配置环境变量

f02b25e185848b137f5323c4f4b5a998.png

3b84ea5b9df4add5aca0b8a504c197ee.png

30b83d9a27f894fabb2dd481d571701f.png

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

检验是否安装成功

fbea42681cbe88b99e493ce37568f5b6.png

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也是可以解压的,解压后如下

70433e9bee7e48c539a9a58e05ab4b82.png

在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如下

<?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发布后带来了新特性之一就是对构建镜像的便捷支持-->
            <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版本,其对应的Spring Boot版本必须是2.6.2,以上只是一个最基本的配置案例,实际开发中还需要在spring-boot-maven-plugin插件的configuration标签下配置其他许许多多的参数。

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

5f3684a781917b2dc7241a0acd8bf8d8.png

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

<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>

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

官方文档:

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%+。推荐面试宝典:https://www.yoodb.com

构建成功

dbf9d72d795202dc325aaa3c610893da.png

7 创建并运行容器

查看所有镜像

432183323d3c32fba9eacaffac67d839.png

spring-native就是构建的镜像。架构设计系列文章:https://www.yoodb.com/framework/knowledge-hierarchy.html

创建并运行容器

65d7660e1ee8dff1c83012f7b311cb73.png

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

0917bc8e54fb9eb54231e3882c922de5.png

成功调用接口

1dac0f2fa962ef3829be4091e9556fd4.png

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

b937b3e6dbcfec571d0eeca541afb049.png

不使用Spring Native启动应用

04edb1666980a74c01fbc7b8b43ccdd7.png

7b177ca3fffd5e3d0c7d790377d64c1e.png

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

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

https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/index.html

 
 
 
 

作者:pcdd

https://blog.csdn.net/weixin_43553153/article/details/122486769

公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!
最近有很多人问,有没有读者交流群!加入方式很简单,公众号Java精选,回复“加群”,即可入群!

Java精选面试题(微信小程序):3000+道面试题,包含Java基础、并发、JVM、线程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架构设计等,在线随时刷题!
------ 特别推荐 ------
特别推荐:专注分享最前沿的技术与资讯,为弯道超车做好准备及各种开源项目与高效率软件的公众号,「大咖笔记」,专注挖掘好东西,非常值得大家关注。点击下方公众号卡片关注。

点击“阅读原文”,了解更多精彩内容!文章有帮助的话,点在看,转发吧!

最后

以上就是懦弱枕头为你收集整理的这样做优化,实现 0.059s 启动一个SpringBoot项目!的全部内容,希望文章能够帮你解决这样做优化,实现 0.059s 启动一个SpringBoot项目!所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(34)

评论列表共有 0 条评论

立即
投稿
返回
顶部