Welcome to my GitHub
https://github.com/zq2599/blog_demos
Content: Classification and summary of all original articles and supporting source code, involving Java, Docker, Kubernetes, DevOPS, etc.;
About Spring Native
- Spring official blog announced the release of the beta version of Spring Native on March 11, 2021. With Spring Native, spring applications and GraalVM can be integrated into <font color="blue">native image</font>;
- Native image is a technology of GraalVM. It compiles the bytecode of the java application into an executable file, and statically links it with the native library of the JDK. There is no need for a Java virtual machine to run the application. It has integrated memory management and threading. For more information on scheduling and other capabilities, please refer to: https://www.graalvm.org/reference-manual/native-image/
- This article focuses on actual combat, so I won't use too much space to introduce the theory and advantages of Spring Native. Here is a brief summary of several important features:
- Application startup speed does not exceed 100 milliseconds;
- The performance peak is reached at startup (C1, C2 and other means are no longer available)
- Lower memory consumption during runtime;
- The docker image does not contain JDK (the required files have been extracted and put into the image). The official image size of Spring Boot, Spring MVC, Jackson, Tomcat is 50M;
- In order to achieve the previous effect, the price is longer construction time;
What exactly is Spring Native
Personal understanding: Spring Native is a technical solution provided by Spring to produce native images, involving the following key technologies:
- Spring ahead-of-time (AOT) plug-in, AOT processing spring applications, so that the traditional virtual machine class lazy loading no longer exists;
- The spring-boot-maven-plugin plugin uses the image named <font color="blue">dmikusa/graalvm-tiny</font> as the build tool when building the docker image. This tool is responsible for building the current project The result is integrated with GraalVM and finally made into a native image;
Overview of this article
As a practical style article, the main content of this article is to develop a springboot application and then build it as a native image, and then verify its function and effect. This article consists of the following content:
- Environmental information
- Create a new maven parent project named spring-native-tutorials, and make a unified configuration for the dependent libraries and plug-ins used in actual combat;
- Create a new maven subproject named webmvc, which is a springboot application;
- Build webmvc as a native image, which is a docker image;
- Start the mirror in docker, verify whether it is available, and check related indicators;
Environmental information
The environmental information related to this actual combat is as follows:
- Computer: MacBook pro 13-inch 2018
- Operating system: macOS Big Sur 11.2.3
- IDE:IntelliJ IDEA 2018.3.5 (Ultimate Edition)
- docker:20.10.5
- JDK:1.8.0_211
- maven:3.6.0
- springboot:2.5.0-SNAPSHOT
- spring-aot-maven-plugin:0.10.0-SNAPSHOT
Source download
- The complete source code in this actual combat can be downloaded on GitHub. The address and link information are shown in the following table ( https://github.com/zq2599/blog_demos):
name | Link | Remark |
---|---|---|
Project homepage | https://github.com/zq2599/blog_demos | The project's homepage on GitHub |
git warehouse address (https) | https://github.com/zq2599/blog_demos.git | The warehouse address of the source code of the project, https protocol |
git warehouse address (ssh) | git@github.com:zq2599/blog_demos.git | The warehouse address of the source code of the project, ssh protocol |
- There are multiple folders in this git project. The source code of this actual combat is under the <font color="blue">spring-native-tutorials</font> folder, as shown in the red box in the following figure:
Create a new maven parent project named spring-native-tutorials
- The learning of Spring Native is not finished by writing helloworld, so here is a parent project to provide unified dependency library and plug-in management for all future applications;
- Create a new maven parent project named <font color="blue">spring-native-tutorials</font>. The content of pom.xml is as follows. There are several points to note that will be mentioned later:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<modules>
<module>webmvc</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0-SNAPSHOT</version>
<relativePath/>
</parent>
<groupId>com.bolingcavalry</groupId>
<artifactId>spring-native-tutorials</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<!-- springboot生成jar文件的文件名后缀,用来避免Spring Boot repackaging和native-image-maven-plugin插件之间可能存在的冲突 -->
<classifier/>
<!-- 构建镜像时的定制参数 -->
<native.build.args/>
<!-- 指定使用dmikusa/graalvm-tiny这个镜像作为构建工具,来构建镜像 -->
<builder>dmikusa/graalvm-tiny</builder>
<!-- spring cloud版本 -->
<spring-cloud.version>2020.0.2</spring-cloud.version>
</properties>
<!-- 插件管理 -->
<pluginRepositories>
<pluginRepository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestone</id>
<name>Spring milestone</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-snapshot</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<!--仓库管理-->
<repositories>
<repository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestone</id>
<name>Spring milestone</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<!--依赖包版本管理-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>0.10.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--插件配置-->
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>${classifier}</classifier>
<image>
<builder>${builder}</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
<BP_NATIVE_IMAGE_BUILD_ARGUMENTS>${native.build.args}</BP_NATIVE_IMAGE_BUILD_ARGUMENTS>
</env>
<!--执行构建任务的镜像,如果在当前环境不存在才会远程下载-->
<pullPolicy>IF_NOT_PRESENT</pullPolicy>
</image>
</configuration>
</plugin>
<!-- aot插件,ahead-of-time transformations -->
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>0.10.0-SNAPSHOT</version>
<executions>
<execution>
<id>test-generate</id>
<goals>
<goal>test-generate</goal>
</goals>
</execution>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
- The above pom.xml has the following points to note:
- The configuration of plug-in warehouse, dependent library warehouse, and dependent library version are all concentrated here;
- Configure the two plug-ins, spring-aot-maven-plugin and spring-boot-maven-plugin, which will be used by the subproject;
- When the spring-boot-maven-plugin plug-in makes the docker image, it will use the <font color="blue">dmikusa/graalvm-tiny</font> image, which is the tool to build the native image;
Create a new springboot type maven subproject
- Create a new sub-project named <font colot="blue">webmvc</font>. The content of pom.xml is as follows. It can be seen that the content is very simple. It is the two plug-ins that are conventionally dependent on the library and the parent project configuration. One is responsible for executing AOT and the other is Responsible for building the image:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-native-tutorials</artifactId>
<groupId>com.bolingcavalry</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>webmvc</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.tomcat.experimental</groupId>
<artifactId>tomcat-embed-programmatic</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<configuration>
<removeSpelSupport>true</removeSpelSupport>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- The code is very simple, an ordinary springboot application with http interface:
package com.bolingcavalry.webmvc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
@SpringBootApplication
@RestController
public class WebmvcApplication {
public static void main(String[] args) {
SpringApplication.run(WebmvcApplication.class, args);
}
@ResponseStatus(HttpStatus.ACCEPTED)
@GetMapping("/status")
public String status() {
return "status";
}
@GetMapping("/")
public String hello() {
return "1. Hello from Spring MVC and Tomcat, " + LocalDateTime.now();
}
}
- Now that the coding is complete, let's build the docker image, enter the directory where the pom.xml of the parent project is located, and execute the following command:
mvn clean -U -DskipTests spring-boot:build-image
- After the build is successful, the output information is as follows (the space is limited only to intercept the last short paragraph), which takes 4 minutes and 25 seconds, during which the notebook fan spins wildly:
...
[INFO] Successfully built image 'docker.io/library/webmvc:1.0-SNAPSHOT'
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for spring-native-tutorials 1.0-SNAPSHOT:
[INFO]
[INFO] spring-native-tutorials ............................ SUCCESS [ 1.786 s]
[INFO] webmvc ............................................. SUCCESS [04:19 min]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 04:25 min
[INFO] Finished at: 2021-05-22T16:36:44+08:00
[INFO] ------------------------------------------------------------------------
[WARNING] The requested profile "nexus" could not be activated because it does not exist.
- Execute the <font color="blue">docker images</font> command, as shown in the figure below, it can be seen that the image has been generated:
- Looking at the mirror composition, it can be seen that each layer is not big, with a total of more than 70 M:
(base) zhaoqindeMBP:~ zhaoqin$ docker history webmvc:1.0-SNAPSHOT
IMAGE CREATED CREATED BY SIZE COMMENT
b8ff54813ae0 41 years ago 69B
<missing> 41 years ago 452kB
<missing> 41 years ago 2.51MB
<missing> 41 years ago 57.2MB
<missing> 41 years ago 1.4MB
<missing> 41 years ago 268B
<missing> 41 years ago 17.3MB
- The mirror image is successfully built, and the basic functions can be verified;
verify
- Execute the following command to create a temporary container (the container will be cleaned up after the console ends):
docker run --rm -p 8080:8080 webmvc:1.0-SNAPSHOT
- The console output is as follows, the startup is completed in 79 milliseconds, which is really a blink of an effort:
(base) zhaoqindeMBP:~ zhaoqin$ docker run --rm -p 8080:8080 webmvc:1.0-SNAPSHOT
2021-05-22 09:34:57.578 INFO 1 --- [ main] o.s.nativex.NativeListener : This application is bootstrapped with code generated with Spring AOT
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.0-SNAPSHOT)
2021-05-22 09:34:57.586 INFO 1 --- [ main] c.b.webmvc.WebmvcApplication : Starting WebmvcApplication using Java 1.8.0_292 on 3529ec458896 with PID 1 (/workspace/com.bolingcavalry.webmvc.WebmvcApplication started by cnb in /workspace)
2021-05-22 09:34:57.586 INFO 1 --- [ main] c.b.webmvc.WebmvcApplication : No active profile set, falling back to default profiles: default
2021-05-22 09:34:57.661 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
May 22, 2021 9:34:57 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-8080"]
May 22, 2021 9:34:57 AM org.apache.catalina.core.StandardService startInternal
INFO: Starting service [Tomcat]
May 22, 2021 9:34:57 AM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet engine: [Apache Tomcat/9.0.46]
May 22, 2021 9:34:57 AM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring embedded WebApplicationContext
2021-05-22 09:34:57.669 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 79 ms
May 22, 2021 9:34:57 AM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
2021-05-22 09:34:57.713 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-05-22 09:34:57.713 INFO 1 --- [ main] c.b.webmvc.WebmvcApplication : Started WebmvcApplication in 0.178 seconds (JVM running for 0.19)
2021-05-22 09:34:57.713 INFO 1 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state LivenessState changed to CORRECT
2021-05-22 09:34:57.714 INFO 1 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
- The browser accesses port 8080 of the machine, as shown in the figure below, the basic functions of the application are normal:
- Look at the resource usage again, the command is <font color="blue">docker stats</font>, as can be seen as follows, the memory is only used 30M:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
6ce6c66fb4de jovial_hertz 0.11% 30.69MiB / 3.844GiB 0.78% 1.49kB / 158B 4.31MB / 0B 18
- I once put a traditional springboot application image <font color="blue">bolingcavalry/hellojib:0.0.1-SNAPSHOT</font> on hub.docker.com, and now I compare it with the Spring Native image. The startup information is as follows, which takes 2036 milliseconds:
(base) zhaoqindeMacBook-Pro:~ zhaoqin$ docker run --rm -P docker.io/bolingcavalry/hellojib:0.0.1-SNAPSHOT
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.6.RELEASE)
2021-05-22 11:13:28.121 INFO 1 --- [ main] c.b.hellojib.HellojibApplication : Starting HellojibApplication on ffb32e5b68b9 with PID 1 (/app/classes started by root in /)
2021-05-22 11:13:28.128 INFO 1 --- [ main] c.b.hellojib.HellojibApplication : No active profile set, falling back to default profiles: default
2021-05-22 11:13:30.000 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-05-22 11:13:30.054 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-05-22 11:13:30.054 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.21]
2021-05-22 11:13:30.241 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-05-22 11:13:30.241 INFO 1 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2036 ms
2021-05-22 11:13:30.715 INFO 1 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-05-22 11:13:31.103 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-05-22 11:13:31.110 INFO 1 --- [ main] c.b.hellojib.HellojibApplication : Started HellojibApplication in 3.618 seconds (JVM running for 4.297)
2021-05-22 11:13:48.866 INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-05-22 11:13:48.866 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-05-22 11:13:48.880 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 14 ms
- Using <font color="blue">docker stats</font> to compare memory, the traditional springboot application container consumes more than 300 megabytes of memory:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
ffb32e5b68b9 eager_williamson 0.64% 356.3MiB / 3.844GiB 9.05% 3.46kB / 2.29kB 0B / 0B 31
6ce6c66fb4de jovial_hertz 0.11% 30.69MiB / 3.844GiB 0.78% 1.49kB / 158B 4.31MB / 0B 18
- In summary, the advantages of Spring Native are obvious, but <font color="blue"> , please pay attention to </font>: The Spring Native officially announced on March 11, 2021 is only a beta version.< font color="red">Please do not use it in a production environment! ! ! </font>
Failed to download plugin
In the actual operation process, you often encounter the failure of downloading the maven plugin or docker image. In addition to trying a few more times, you can also consider putting the project on github and using github action to complete the image building in the cloud. For specific operations, please refer to "Create Docker image with GitHub Actions"
No development, direct experience
- I have uploaded the image to hub.docker.com, the full name is <font color="blue">bolingcavalry/webmvc:1.0-SNAPSHOT</font>, if you just want to experience the effect of the native image, you can download the image directly use;
You are not alone, Xinchen and original are with you all the way
- Java series
- Spring series
- Docker series
- kubernetes series
- database + middleware series
- DevOps series
Welcome to pay attention to the public account: programmer Xin Chen
Search "Programmer Xin Chen" on WeChat, I am Xin Chen, and I look forward to traveling the Java world with you...
https://github.com/zq2599/blog_demos
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。