文章目录
前言
上一篇:GraalVM学习笔记
上一节简单介绍了GraalVM以及它的配置安装,其中提到了它的Native Image的构建,这一节将使用Quarkus框架来实战一个CRUD应用,并将其构建为Native Image来感受这技术的魅力
为什么要构建Native Image
小伙伴们可能遇到过这种场景,每次开发的接口刚上线的时候,可能会有一小部分请求超时,虽然大部分情况下不影响,但是看着心里还是有点不爽。。。
这就是Hotspot的预热问题,我们知道Hotspot使用了JIT(Just-in-time)即时编译技术,针对热点代码可以编译为高度优化的机器码,提高执行效率(这时候效率不输于C++写的代码),但在编译之前java代码是以相对低效的解释器模式运行的。所以在刚启动应用时,如果业务流量比较高,大量java方法开始被JIT编译,同时以解释器方式执行,造成系统负载飙高,导致了一些请求超时。
怎么解决?
1、目前针对这个很多做法是使用模拟流量来提前预热应用,再就是在Alibaba Dragonwell(阿里订制JDK)的Jwarmup模块中,利用了Java 虚拟机前一次执行JIT编译得记录的元数据来预热本次应用的执行,可以大大加速JIT的预热(阿里还是很牛逼的!)
参考:
https://blog.csdn.net/yunqiinsight/article/details/89138911
2、而对于不需要长时间运行的,或小型化的应用而言,特别是近几年从大型单体应用架构向小型微服务应用架构发展的技术潮流下,java天生带有一些劣势。
在微服务架构下,应用拆分后,单个微服务的功能单一,可能不需要7x24小时运行,应该可以随时中断更新甚至动态扩容,在Serveless里面矛盾更加突出,比如现在流行的FaaS云函数调用,都是传统java的弱项。
而现在部署都流行容器部署,里面可以确保环境一致,那么java的跨平台性在这里显得不是那么重要,快速启动应用、降低内存使用显得相对重要一些了。所以这时候可以通过GraalVM的Native Image编译为一个二进制可执行文件,直接使用GraalVM提前编译涉及到的配置比较繁琐,不过现在有比较成熟的框架做这个事情,比如Quarkus、Helidon、Micronaut等等,还有目前正在快速迭代的Spring-graalvm-native(期待这个的发展)
Native image模式下微服务的性能比较https://medium.com/graalvm/lightweight-cloud-native-java-applications-35d56bc45673
图上可以看出在native模式下,内存占用特别小
什么是云原生(Cloud Native)
一说到quarkus、native image、容器这些就会联系到云原生,可以说这些都是构建云原生的基础吧,这里简单记一下
目前来说,云原生(Cloud Native)是一种构建和运行应用程序的方法,是一套技术体系和方法论,是一个思想的集合,Cloud Native既包含技术(微服务,敏捷基础设施),也包含管理(DevOps,持续交付,康威定律,重组等)。Cloud Native也可以说是一系列技术、企业管理方法的集合。
云原生(Cloud Native)是一个组合词,Cloud+Native。Cloud表示应用程序位于云中,而不是传统的数据中心;Native表示应用程序从设计之初即考虑到云的环境,原生为云而设计,在云上以最佳姿势运行,充分利用和发挥云平台的弹性+分布式优势。
参考:
https://cloud.tencent.com/developer/article/1583992
总结
通过构建native image解决了传统java 的几个痛点
1、内存占用大大减小(听运维同学以前抱怨过java很吃内存,不过一个spring boot启动通常消耗百M内存)
2、冷启动速度也会大大加快(回车键下去立马有输出,传统java程序回车下去会出现一段“空白期”)
3、特别适合容器化(docker、k8s)部署
4、是构建云原生的基础
下面就来实操实操!
Quarkus
官方教程: https://quarkus.io/guides/
Github: https://github.com/quarkusio/quarkus
Start: https://code.quarkus.io/
发现quarkus这个框架比spring boot更轻量级,可以构建native应用,在生产中可以把它用在小的、功能单一的程序上。看官方文档后,感觉很多用法和功能跟spring boot很类似,如果熟悉spring boot,入手quarkus也非常快。
实际生产中,我这里将一些简单的中转程序、对外接口程序、订制版定时任务,只涉及简单的CRUD和web调用的,用了quarkus试试水,目前运行挺稳定的
环境要求:java1.8、maven3.6.3(官方要求3.6.2+)、GraalVM20.3(用于构建native,目前quarkus最新版需要java11版本)、quarkus1.10.5
这里注意,如果要构建native,Quarkus对Maven、GraalVM、Java版本有一定的要求
参考文档: https://quarkus.io/guides/building-native-image
HelloWorld-第一个web应用
这里按照官方文档来开始实操
去https://code.quarkus.io/构建一个项目,或者在Iidea上创建Quarkus的项目
第一次看到这么多陌生的组件有点慌,慢慢来,它用的是java EE(现在更名为了JakartaEE)的那套东西。
这里我们就勾选RESTEasy JAX-RS即可
组件: JAX-RS(这是web应用必须的)
创建一个helloworld程序大致分为下面几步
1、创建启动模板
2、创建JAX-RS endpoint,类似于spring的controller层
3、注入bean
4、启动运行
5、编译为二进制可执行程序
现在一步一步来介绍
1、创建启动模板
这点在上面已经介绍了,就是在https://code.quarkus.io/上面下载一个模板,或者Idea创建Quarkus项目
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.acme</groupId>
<artifactId>test2</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<compiler-plugin.version>3.8.1</compiler-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus-plugin.version>1.5.2.Final</quarkus-plugin.version>
<quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
<quarkus.platform.version>1.5.2.Final</quarkus.platform.version>
<surefire-plugin.version>2.22.1</surefire-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
</project>
项目创建好后,它会创建一个这样的pom文件
2、创建JAX-RS endpoint
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
这里可以看到resteasy,就是之前选择的JAX-RS(Java API for RESTful Web Services),它是基于JAX-RS标准实现的框架,类似的还有Jersey。简单来说它是一种RESTful Web 服务,类比于Spring MVC,但跟Spring MVC使用的注解不同(后面有个实验性项目quarkus-spring-web可以转换用Spring MVC的注解来写)
大致注解有:
@Path,标注资源类或者方法的相对路径 @GET,@PUT,@POST,@DELETE,标注方法是HTTP请求的类型。
@Produces,标注返回的MIME媒体类型 @Consumes,标注可接受请求的MIME媒体类型
@PathParam,@QueryParam,@HeaderParam,@CookieParam,@MatrixParam,@FormParam,分别标注方法的参数来自于HTTP请求的不同位置,例如@PathParam来自于URL的路径,@QueryParam来自于URL的查询参数,@HeaderParam来自于HTTP请求的头信息,@CookieParam来自于HTTP请求的Cookie。
返回对象推荐使用自带的Response类javax.ws.rs.core.Response,重定向也是靠这个类去做
package org.acme;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "hello";
}
}
这是一个非常简单的 REST 端点(controller),对/hello上的请求返回 hello
3、注入bean
Quarkus 的依赖注入基于 ArC,这是一个基于 CDI 的依赖注入解决方案(这玩意也是Java EE的规范),关于CDI的注入方式的例子网上有很多,这里就不赘述了
Arc是quarkus-resteasy的依赖,所以不用专门引入它
让我们来创建一个service
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class GreetingService {
public String greeting(String name) {
return "hello " + name;
}
}
加上一个ApplicationScoped注解,类似于@Component
import org.jboss.resteasy.annotations.jaxrs.PathParam;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/hello")
public class GreetingResource {
@Inject
GreetingService service;
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("/greeting/{name}")
public String greeting(@PathParam String name) {
return service.greeting(name);
}
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "hello";
}
}
还是刚刚的controller,使用@Inject就可以注入进来了,类似于@Autowire
4、启动运行
通过命令: mvn quarkus:dev 启动dev
然后就成功启动了
输入http://localhost:8080/hello/greeting/0x 访问我们的接口
发现成功返回了
5、编译为二进制可执行程序
打jar包使用mvn package就可以了,这里重点介绍构建Native Image,这是它最有趣的功能
参考:https://quarkus.io/guides/building-native-image
环境:
1、GraalVM 20.3.0+(配置GraalVM可以参考我的上一篇博文 https://blog.csdn.net/w57685321/article/details/111872542)
2、C语言环境,安装
# dnf (rpm-based)
sudo dnf install gcc glibc-devel zlib-devel libstdc++-static
# Debian-based distributions:
sudo apt-get install build-essential libz-dev zlib1g-dev
最好在linux上打包,坑比较少,我这里使用虚拟机去打包
3、至少8G以上内存(之前因为虚拟机内存配少了导致OOM)
4、maven环境(可以参考我之前的https://blog.csdn.net/w57685321/article/details/111823483)
然后将程序拖过去就可以开始打包
mvn package -Pnative -DskipTests
打包有点慢,稍微等待一下
发现启动贼快,回车下去程序马上跑起来
它还可以直接构建为容器运行,有兴趣的小伙伴可以参考文档: https://quarkus.io/guides/building-native-image#creating-a-container
Jdbc应用
基于上一节的helloworld继续添加jdbc功能
quarkus配置
配置jdbc离不开读取配置文件,这里先介绍quarkus的配置
可以通过这个命令生成一个样例配置:
mvn quarkus:generate-config
发现通过这个命令生成了一个模板配置文件,说明注释也很详细
支持yaml
平常我们使用yml可能更多一些,这里也支持yml,首先引入包
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-config-yaml</artifactId>
</dependency>
然后把application.properties改为yml就可以愉快的使用yml进行配置了
quarkus:
application:
name: test1
version: 1.0
http:
port: 8081
配置注入
提供了类似spring boot中@Value、@ConfigurationProperties的功能
@ConfigProperty(name = "greeting.message")
String message;
使用@ConfigProperty注入单个属性,跟spring boot的@Value一样
@ConfigProperties(prefix = "greeting")
public class GreetingConfiguration {
}
注入配置到类,跟spring boot的@ConfigurationProperties一样,还支持嵌套对象,如果觉得不习惯可以用quarkus-spring-boot-properties转换
配置jdbc
现在可以配置我们的jdbc连接了,准备使用Hibernate做orm,首先引入依赖
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-mysql</artifactId>
</dependency>
然后配置yml
quarkus:
application:
name: test1
http:
port: 8081
datasource:
db-kind: mysql
username: xxx
password: xxx
jdbc:
url: jdbc:mysql://10.101.167.28:3306/jf_test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=round
driver: com.mysql.cj.jdbc.Driver
创建一张测试表
写crud代码
构建entity
@Data
@Entity
@Table(name = "test")
public class Test {
@Id
private String test1;
private String test2;
}
构建repository
@ApplicationScoped
public class TestRepository implements PanacheRepository<Test> {
}
构建service
@ApplicationScoped
public class TestService {
@Inject
private TestRepository testRepository;
public List<Test> selectAllTests() {
return testRepository.findAll().list();
}
}
构建controller
@Path("/hello")
public class ExampleResource {
@Inject
private TestService testService;
@GET
@Path("/test1")
@Produces(MediaType.TEXT_PLAIN)
public List<Test> hello1() {
return testService.selectAllTests();
}
}
老实说这玩意的用法很像spring data jpa,既然这么多地方用法类似(spring-data-jpa、spring-boot-properties、spring-web、spring-di),就有办法将spring的注解用进来,这也是下一节将要介绍的内容
启动日志会打印目前使用到的功能模块,同样类似上一节的,构建成native应用后,启动飞快0.052s,占用内存只有30M+
使用Spring注解
参考文档:https://quarkus.io/guides/spring-web
第一次使用quarkus的注解确实不是很习惯,这里提供了一系列的工具来支持spring注解的方式配置,这也是我喜欢用的方式
注意这个目前还是预览版
关于spring依赖注入与CDI的区别
Spring | CDI / MicroProfile | Comments |
---|---|---|
@Autowired | @Inject | |
@Qualifier | @Named | |
@Value | @ConfigProperty | 区别就是@ConfigProperty不支持@Value的表达式语言 |
@Component | @Singleton | 默认情况下,Spring的是单例 bean |
@Service | @Singleton | 默认情况下,Spring的是单例 bean |
@Repository | @Singleton | 默认情况下,Spring的是单例 bean |
@Configuration | @ApplicationScoped | 在 CDI 中,一个生产者 bean 并不局限于application scope,它也可以是@singleton 或@dependent |
@Bean | @Produces | |
@Scope | 没有对应的CDI注解。根据@scope 的值,可以使用@singleton、@applicationscoped、@sessionscoped、@requestscoped 和@dependent |
关于spring web注解与JAX-RS注解的区别
Spring | JAX-RS | Comments |
---|---|---|
@RequestController | 在 JAX-RS 中没有等价的注解,用@Path就足够了 | |
@RequestMapping(path="/api") | @Path("/api") | |
@RequestMapping(consumes=“application/json”) | @Consumes(“application/json”) | |
@RequestMapping(produces=“application/json”) | @Produces(“application/json”) | |
@RequestParam | @QueryParam | |
@PathVariable | @PathParam | |
@RequestBody | 在 JAX-RS 中没有等价的注解,JAX-RS处理这种类型的不需要任何注解 | |
@RestControllerAdvice | 在 JAX-RS 中没有等价的注解 | |
@ResponseStatus | 在 JAX-RS 中没有等价的注解 | |
@ExceptionHandler | 在 JAX-RS 中没有等价的注解,异常通过实现 javax.ws.rs.ext. ExceptionMapper 来处理 |
实操
使用Idea的Quarkus新建一个工程,Idea对Quarkus提供了很好的支持
在Compatibility里面,有Extension for Spring的一些列,把需要的加上即可
各种框架对比:
Spring DI -> CDI
Spring web ->JAX-RS
Spring data jpa -> Hibernate ORM with Panache
我这里勾选后生成的Pom
<properties>
<surefire-plugin.version>2.22.1</surefire-plugin.version>
<maven.compiler.target>8</maven.compiler.target>
<quarkus.platform.version>1.10.5.Final</quarkus.platform.version>
<maven.compiler.source>8</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.parameters>true</maven.compiler.parameters>
<quarkus-plugin.version>1.10.5.Final</quarkus-plugin.version>
<compiler-plugin.version>3.8.1</compiler-plugin.version>
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-di</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-web</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-mysql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus-plugin.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>
${project.build.directory}/${project.build.finalName}-runner
</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager
</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
直接启动即可,一个默认的helloworld页面就出来了
然后就可以用spring的方式去写程序了
这里贴一些主要代码,看看就明白了
配置yml的数据库地址
quarkus:
application:
name: testApi
version: 1.0
http:
port: 12110
datasource:
db-kind: mysql
username: ooo
password: ooo
jdbc:
url: jdbc:mysql://127.0.0.1:3306/db1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=round
driver: com.mysql.cj.jdbc.Driver
custom:
config:
address1: http://10.113.251.147:8080/test/servlet/report?
address2: http://10.113.251.147:8080/test/mms/http/download?
Repository的用法,这个跟spring data jpa使用一样,支持大部分语法(还没有全部支持,比如distinct)
public interface StaffRepository extends CrudRepository<SysStaff4aRel, Long> {
List<SysStaff4aRel> findFirstByCode(String code);
}
controller的用法,这里RequestParam需要指定name,不然传参为null
返回对象、重定向什么的直接用它提供的javax.ws.rs.core.Response
@RestController
public class AiBassapiController {
@Autowired
private AibassapiService aibassapiService;
@GetMapping("report")
public Response report(@RequestParam(name = "token") String token) throws IOException {
if (StringUtils.isNotBlank(token)) {
token = token.replace(' ', '+');
Result result = aibassapiService.jfreportSendRedirect(token);
if (result.isState()) {
return Response.temporaryRedirect(URI.create((String) result.getContent())).build();
} else {
return Response.ok("token不正确!").build();
}
} else {
return Response.ok("未登录!").build();
}
}
}
Service的用法也跟spring一样的
@Slf4j
@Service
public class AibassapiService {
@Autowired
StaffRepository staffRepository;
@Autowired
AddressConstant addressConstant;
…………
}
至于其他的用法推荐查看官方文档https://quarkus.io/guides/spring-web
最后打包之后运行,启动速度很快
发现RSS实际使用内存大小很小,只有40多M
Spring GraalVM Native
我们平常使用Spring Boot用的最多,对这个也是最熟悉的,如果Spring boot也像Quarkus一样,提供一个Native Image的功能就好了,所以目前spring也在开发一个实验性项目,提供了对GraalVM的支持
参考文档 https://repo.spring.io/milestone/org/springframework/experimental/
Github https://github.com/spring-projects-experimental/spring-graalvm-native
spring-graalvm-native是一个将spring boot构建native应用的插件,正常来说,直接使用graalVM的native-image肯定是不行的,spring boot里面用到了大量反射、动态代理等技术,而native应用不支持这些特性,需要进行特殊处理
注意:目前这个框架还处于试验阶段,在逐渐完善中,有许多bug,最好不要用于线上生产!
实操
使用的版本
Spring boot 2.4.0
spring-graalvm-native 0.8.3
GraalVM 20.3.0
我这用的centos7.3,8G内存(官方说的内存推荐16G+,小了可能会报错)
参考git源码中的spring-graalvm-native-samples目录
然后文档中有两种构建native,一种需要docker,另一种就是我们用的方式,通过本地的GraalVM
1、首先配置pom
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-graalvm-native</artifactId>
<version>0.8.3</version>
</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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>20.3.0</version>
<configuration>
<mainClass>com.ai.bassapi.AibassapiApplication</mainClass>
<buildArgs>-Dspring.native.remove-yaml-support=true -Dspring.spel.ignore=true</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>
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<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-snapshot</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</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>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<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-snapshot</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</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>
</pluginRepositories>
mainClass换成我们的spring boot的入口类,
- Dspring.native.remove-yaml-support = true 和-Dspring.spel.ignore = true 禁用yml和spel表达式减小内存占用
2、mvn -Pnative clean package命令打包即可
可以发现一个简单的web应用就成功启动起来了,按下回车马上运行完毕,启动时间0.18s
注:
这里没有详细贴代码和配置,因为目前这个框架还是处于实验性阶段,非常不稳定,相关配置随时在变化,几个月前是用的0.7版本,比现在要多配置很多东西,也会报各种错误,所以现在详细贴代码和配置没有参考意义,如果需要可以参考官方文档及demo,里面还有其他的功能比如data jpa、redis等等。这里就了解体验一下这门有趣的技术,期待它逐渐完善!