Spring Native 0.11.1入门体验

文章目录

前言

最近用Spring Alibaba做了一个微服务的项目,部署的时候遇到了难题:内存不够。目前该项目有7个微服务,因为我只有一台阿里云的服务器(2C 4G),我只能把所有的微服务全部部署在一台服务器上(经济条件有限),部署方式是使用docker制作springboot的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变的更“原生”。
Spring Native 0.11.1入门体验

实战

本次实战相关的环境信息如下:
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 0.11.1入门体验
从官方文档得知(上图)
使用 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标签下,用过fabric8或spotify的docker-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/
Spring Native 0.11.1入门体验
Spring Native 0.11.1入门体验

2 配置环境变量

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

检验是否安装成功
Spring Native 0.11.1入门体验

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也是可以解压的,解压后如下
Spring Native 0.11.1入门体验
在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参数为例
Spring Native 0.11.1入门体验

通过官方文档(使用了chrome自带的翻译功能)得知只需要在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%+,
构建成功
Spring Native 0.11.1入门体验

7 创建并运行Docker容器

查看所有镜像
Spring Native 0.11.1入门体验
spring-native就是构建的镜像
创建并运行容器Spring Native 0.11.1入门体验
在Docker Desktop查看,发现应用成功启动,启动仅耗时0.059s,也就是59ms,果然印证了Spring Native启动是毫秒级别这句话。
Spring Native 0.11.1入门体验
调用接口,成功
Spring Native 0.11.1入门体验
在Docker Desktop查看占用内存,仅28M左右
Spring Native 0.11.1入门体验
不使用Spring Native启动应用
Spring Native 0.11.1入门体验
Spring Native 0.11.1入门体验
启动耗时3s,占用内存高达511M,高下立判。
文章仅供参考,建议结合Spring Native官方最新文档学习。
https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/index.html

上一篇:android一个上传图片的样例,包含怎样终止上传过程,假设在上传的时候更新进度条(一)


下一篇:Spring Native实战(畅快体验79毫秒启动springboot应用)