Spring Native 0.11.1 入门体验(Spring Boot 2)

spring-native 仓库于 2023.2.24 归档,现已被 Spring Boot 3 + 官方原生支持取代。本文讲述的是 Spring Boot 2 如何使用 Spring Native

https://github.com/spring-attic/spring-native

最新官方文档,比较详细

https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.developing-your-first-application

前言

自己曾用 SCA 做了一个学习微服务的项目,部署的时候遇到了难题:内存不够。目前该项目有 7 个微服务,只有一台服务器(2C4G),只好把所有的微服务都部署在一台服务器上(强人所难),部署方式是使用 Docker 制作 fat jar 镜像,每个微服务默认内存约占 500M,SCA 还要部署:Nacos,除此之外还用到了 Redis、Sentinel、MQ、ELK 等(MySQL 上云),光是运行这些应用就占用 2G+ 内存,剩下 1G+ 内存在部署 4 个微服务后就满了,于是开始对应用的内存进行初步优化:

添加 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 数量,所以这个参数可省略。
配置完成后,启动服务发现内存确实变小了,由原来的 500M 降至 100~200M,但我期望的效果是达到几十 M 的级别。
经网上查阅资料得知可以使用 Spring Native 这门新技术来实现。该技术正处于快速迭代阶段,变动较大

Spring Native 使用效果

  1. 应用启动速度特别快,毫秒级别
  2. 运行时更低的内存消耗,官方展示的含有 Spring Boot, Spring MVC, Jackson, Tomcat 的镜像大小是 50M

Spring Native 使用前注意事项

  1. 为了达到前面的效果,代价是构建时间更长,即使是一个 Hello Word 构建也需要 2 分钟(本机),取决于电脑配置。
  2. 若项目中用到的依赖不支持 Spring Native,则无法使用,比如之前的 MyBatis,直接构建将会报错,幸运的是现在 MyBatis 提供了解决方案:https://github.com/mybatis/spring-native
  3. 建议新项目使用,不推荐老项目,重构成本高

Spring Native 是什么

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

摘抄自 GitHub 上 Spring Native 的自述文件:

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

摘抄自其他博客:

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

在这里插入图片描述

实战

环境如下:
OS:Windows 10 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 的应用程序应该使用 Java 11 或 Java 17 编译。
构建 Spring Boot 原生应用程序有两种主要方法:

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

经过各种踩坑后在本机上成功的实践了方法 1 和方法 2:

方法 1 就是在 Spring Boot 2.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 Build Tools 和 Windows SDK(无需安装臃肿的 Visual Studio,仅需用到单个组件:MSVC 和 Windows SDK)

  2. 执行的 maven 命令不同

    方法1是mvn spring-boot:build-image

    方法2是mvn -Pnative native:compile

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

1. 下载合适版本的 graalvm-jdk

https://www.graalvm.org/downloads/
环境变量需设置 JAVA_HOME、GRAALVM_HOME

2. 检验是否安装成功

在这里插入图片描述

3. 安装 native-image

确保安装了 native-image,新版 graalvm 21 默认已安装,若低版本没有则需要安装,将从 GitHub 下载(注意网络环境。gu 是 Graalvm 提供的工具,位于 graalvm-jdk/bin 下。小心和 powershell 的 gu 冲突)

gu.cmd install native-image

4. 安装 Docker Desktop for Windows

按照官方文档操作即可:https://docs.docker.com/desktop/windows/install/
嫌臃肿?这里推荐另一种轻量级安装方案:在 WSL 2 中安装 Docker Engine

5. 配置 pom.xml

创建一个 Spring Boot 项目,命名为 spring-native
完整的 pom.xml 如下

<?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/milestone</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-release</id>
            <name>Spring release</name>
            <url>https://repo.spring.io/milestone</url>
        </pluginRepository>
    </pluginRepositories>
</project>

Spring Native 0.11.1 版本对应的 Spring Boot 版本必须是 2.6.2,以上只是一个简单的配置案例,实际可能还要在 spring-boot-maven-plugin 插件的 configuration 标签下配置其他参数,例如 Docker 远程的地址和证书的路径、JVM 调优参数、配置文件指定、Docker 镜像名端口仓库地址等等,最好的方法就是看 spring-boot-maven-plugin 的官方文档,这里以配置 JVM 参数为例

在这里插入图片描述

通过官方文档得知仅在 configuration 标签下配置即可

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

完整配置参数见:https://docs.spring.io/spring-boot/docs/2.6.2/maven-plugin/reference/htmlsingle/#build-image

6. 执行 maven 命令

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

下载完相关依赖后,电脑风扇就开始呼呼的转,CPU 利用率 100%,内存使用量飙升,最后稳定在 90%。

构建成功

在这里插入图片描述

7. 创建并运行容器

查看所有镜像,spring-native 就是刚构建的镜像

在这里插入图片描述

创建并运行容器

在这里插入图片描述

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

在这里插入图片描述
访问

在这里插入图片描述
在 Docker Desktop 查看占用内存,仅 28M 左右。

在这里插入图片描述

不使用 Spring Native 启动应用

在这里插入图片描述
在这里插入图片描述

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

文章仅供参考!建议结合 Spring Native 最新官方文档学习。
https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.developing-your-first-application

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值