在上一篇关于GraalVm的文章中,我们看到了在使用GraalVm时polyglot功能是如何工作的。我们看到了在一个应用程序中如何将多种语言绑定在一起。此外,我们还了解了如何将变量和状态从一种编程语言传递到另一种编程语言。在这篇文章中,我们将介绍使用GraalVm的另一个方面:更低的内存占用和更快的启动时间。GraalVm使用本机映像(镜像)的概念来支持较低的内存占用和非常快的应用程序启动时间。因此,我们这里的重点是将spring-boot应用程序作为Jvm上的普通jar和grall上的本机映像运行。我们将捕获和度量,如启动时间的提升等,让我们开始吧。
先决条件
因此,在我们继续前进之前,您应该了解以下一些基本知识:
- springbootweb应用程序的基本知识,比如如何创建和运行一个。
- 对GraalVm的一些基本理解。
- GraalVm的设置已在本地完成,为此,您可以按照以下说明操作:https://blog.knoldus.com/going-polyglot-with-graalvm/
- 熟悉maven和Java编程语言。
仅此而已。现在,让我们分析一下什么是原生图像。
本机映像 Native Image
我相信大多数开发人员都经历了等待应用程序启动时间过长的痛苦。根据应用程序的大小和复杂性,启动时间可能会有所不同。因此,有时应用程序需要30-40秒才能启动和运行。现在想想,如果你必须重复相同的过程n次,需要多少时间?是的,我想你现在到了。痛苦是真实的。在启动过程中,坐在空闲的地方,看着应用程序每次加载类和jar并不是任何人都愿意看到的。我们能缩短启动时间吗?我们能减少应用程序的内存占用吗?有没有一种方法,我们可以加载必要的类一次,并运行应用程序直接不重新加载他们在每一个新的运行?
好吧,围绕着这个话题还有很多其他的问题,GraalVm是对这些问题的回答。怎么用?通过GraalVm的本地图像支持。让我们了解一下本地图像的概念,引用自GraalVm官方文档:
GraalVM本机映像允许您提前将Java代码编译成一个独立的可执行文件,称为本机映像。这个可执行文件包括应用程序类、来自其依赖项的类、来自JDK的运行时库类和来自JDK的静态链接本机代码。它不在javavm上运行,但是包含了一些必要的组件,比如来自不同虚拟机(称为“底层VM”)的内存管理和线程调度。与javavm相比,生成的程序具有更快的启动时间和更低的运行时内存开销。
这解释了很多。因此,是AOT(提前)编译而不是JIT使启动时间更快,并降低了内存开销。总的来说,本机映像是一个实用程序,它处理应用程序的所有类及其依赖项。此实用工具支持基于JVM的语言,如Java、Scala、Kotlin等。
使用本机映像
由于本机映像是一个实用程序,因此默认情况下,它不会与GraalVm安装一起绑定。因此,如果您已经设置了GraalVm home,只需使用以下命令:
gu install native-image
其中gu
是graal实用程序命令提供程序,并安装其他实用程序。
这里需要注意的是,当我们创建spring引导应用程序时,我们需要在参考资料部分下提供一个额外的目录。这是META-INF/native-image
,可以在代码结构中看到。它用于为本机映像生成过程提供不同的配置选项。
让我们继续,创建一个spring boot应用程序。
初始化spring启动应用程序
好吧,让我们快速开发一个足以满足我们用例的小应用程序,请遵循以下步骤:
- 转到Spring initializer,选择Maven,java1.8,并为您的应用程序提供工件id和描述。
- 转到依赖项栏并从列表中添加以下依赖项:
spring-boot-starter-web
spring-boot-starter-test
- 单击Generatetheproject,这将下载项目的Zip文件。
- 将项目提取到您想要的位置,然后导入您喜欢的IDE,我喜欢IntelliJ。
一旦我们添加了一组基本的依赖项,我们就可以继续利用本机映像特定的依赖项。由于本机映像支持仍然是Spring的实验性支持,因此我们需要添加一些额外的依赖项。我们在下面列出:
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-graal-native</artifactId>
<version>0.6.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
</dependency>
spring-graal-native
依赖性将几个组件组合在一起。它包括GraalVM特性实现。
spring-context-indexer
已经在spring中使用了一段时间。本机映像不知道类路径。因此,在运行时探索类路径以查找组件是不可能的。索引器实际上在Java编译时生成一个组件列表,并将其捕获到内置应用程序的spring.components文件中。如果Spring启动并找到这个文件,它将使用它,而不是尝试探索类路径。
我们还需要将外部存储库添加到pom.xml中,以便在应用程序中加载graal支持的二进制文件,请参见:
外部存储库
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
一旦我们设置了存储库和依赖项,我们将转向在pom中添加本机image maven插件。插件如下所示:
<profiles>
<profile>
<id>graal</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>20.0.0</version>
<configuration>
<buildArgs>
-Dspring.graal.remove-unused-autoconfig=true --no-fallback --allow-incomplete-classpath
--report-unsupported-elements-at-runtime -H:+ReportExceptionStackTraces --no-server
-H:IncludeResourceBundles=javax.servlet.http.LocalStrings
-H:IncludeResourceBundles=javax.servlet.LocalStrings
</buildArgs>
</configuration>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
本机映像需要知道应用程序的入口点,我们通过在pom的属性下包含一个<start class></start class>
来定义它。
<start-class>com.knoldus.springbootandgraalvm.SpringBootAndGraalvmApplication</start-class>
这就是我们需要添加到spring boot pom中的所有内容,以便生成graal本机映像并在GraalVm上运行它。
请注意,在spring boot应用程序类中,注释定义为:
@SpringBootApplication(proxyBeanMethods = false)
我们使用了“proxyBeanMethods=false
”,以确保避免在应用程序中使用cglib和任何其他非JDK代理。GraalVm一点也不喜欢他们。
运行应用程序
为了运行应用程序,我们有3种可能的方法:
1. 将其作为一个jar运行,这是一个基于JVM的普通springboot应用程序。
2. 创建一个本机映像,然后在JVM本身上运行它。
3. 在GraalVm上运行本机映像。
我们将逐一查看所有3种可能的方法,并将比较与它们相关的度量。我们开始吧。
在JVM上运行应用程序
要在JVM上作为jar运行,我们只需要在终端中运行下面提到的命令:
mvn clean spring-boot:run
or we can go to springBootApplication class right click on the main method and select run as Java application.
这将开始初始化服务并在端口8080
上运行服务。我们可以查看服务日志编译和启动服务所花费的时间,在不同的机器上可能有所不同。
在JVM上运行本机映像
要运行本机映像,必须首先创建它。请注意,本机映像创建是一个繁重的过程,可能会导致内存不足16G的系统出现一些问题。不过,一旦创建了运行的图像,它就非常直接和简单了。
按照以下步骤在JVM上创建和运行本机映像。
mvn clean package
2. export META=src/main/resources/META-INF
3. mkdir -p $META
4. java -agentlib:native-image-agent=config-output-dir=${META}/native-image -jar target/spring-boot-and-graalvm-0.0.1-SNAPSHOT.jar
这就是您的应用程序将在端口8080
上开始运行的全部内容,我们可以通过调用endpoint:localhost:8080/users/admin
来测试这一点。但是,我们应该对应用程序生成的日志更感兴趣。这一次您可以看到,与JVM平面jar运行相比,服务器启动所花的时间要少得多。
GraalVm上的本地图像
接下来是最后一种方法,这里我们将使用graalmaven插件生成本地图像,然后运行图像。要运行应用程序,我们需要执行以下操作:
1. Package your native image with native image maven plugin:
mvn -Pgraal clean package
This builds the native image, is a lengthy and heavy process so be patient.
2. Run the native image.
Go to the terminal and execute:
./target/com.knoldus.springbootandgraalvm.springbootandgraalvmapplication
你会注意到,只要你输入第2个命令,服务就已经开始运行,您可以在端口8080
上检查它。其余的事情保持不变,尽管这一次服务在瞬间启动。因此,应用程序日志显示了这种方法与其他方法在启动时间上的巨大差异。与其他方法相比,现在可以看到>60%的快速启动时间。
这就是在graalVm上运行本地映像的好处。
结论
我们已经看到了在graalVm上作为本机映像运行spring boot应用程序的好处和性能基准。在不同的机器上,度量标准可能不同,但所获得的性能肯定会让所有人兴奋。
完整代码地址:https://github.com/knoldus/GraalVMSpring
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2114.html
暂无评论