Quarkus 入门
简介
JRE:Java Runtime Environment
JDK:Java Development Kit
JDK 是开发环境
JRE 是运行环境
JDK 包含了JRE
JRE 中包含虚拟机JVM
如果只需要运行Java 程序,那么可以只安装JRE
HotSpot VM 是 JDK 默认内置的 JVM
Graal VM 在 HotSpot JVM 的基础上,增加了一个增强的 JIT(Just-in-Time) 编译器,即 Graal 编译器,以及一个 language implementation framework (Truffle) 用于支持多种语言,使得 Graal VM 成为可以支持多种语言的虚拟机,既支持 Java、Scala、Groovy、Kotlin 等基于 Java 的语言,还支持 C、C++、Rust、JavaScript、Ruby、Python、R 等语言,并且支持不同语言互相调用
Graal 支持 AOT (Ahead of Time Compilation),即提前编译,就是把程序直接编译成二进制直接运行,提升了启动速度,减少了内存使用
在云原生微服务时代,容器化环境要求应用更轻量更快启动,传统的 SpringBoot 程序,将 Jar 包和 Java 环境打包进 image 需要 100 多 M 空间,启动也偏慢
Quarkus 就是为了解决这个问题的,Quarkus 使用 Graal 构建应用
Quarkus 有以下特点
- 容器优先,启动快,体积小
- 满足 Kubernete 云原生要求,比如 一份基准代码多份部署/开发环境与生产环境等价/显式声明依赖关系/无状态运行 等等
- 统一命令式与反应式编程
- 支持 native 模式,即编译出来的可执行文件能直接运行,不需要 JVM
- 提高开发效率,比如支持实时编码/统一配置 等
- 使用 Microprofile 规范构建微服务
下面讲一个简单的例子
创建项目
需要安装设置
- Maven 3.8.1+
- JDK 11+
- JAVA_HOME 指向 JDK 11+
创建命令
apache-maven-3.8.4/bin/mvn io.quarkus:quarkus-maven-plugin:2.4.1.Final:create \
-DprojectGroupId=com.example \
-DprojectArtifactId=quarkus-getting-started \
-DclassName="com.example.demo.resources.GreetingResource" \
-Dpath="/hello"
效果和使用 io.quarkus.platform 创建一样,文件 pom.xml 的 dependencyManagement 用的都是 io.quarkus.platform:quarkus-bom 而不是用 io.quarkus:quarkus-bom,这两个差不多,有少数 dependency 不一样,并且 quarkus 版本是最新的 2.6.1.Final 而不是指定的 2.4.1.Final,不清楚是为什么
这里的 dependencyManagement 起着类似 parent 的作用,使得 dependencies 下的 dependency 可以不用指定版本,直接用 dependencyManagement 引用的 bom 里面的相同 dependency 的版本
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>2.6.1.Final</quarkus.platform.version>
<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>
需要的话就手动把 bom 和 version 换了
初始的用于 src 代码的 dependency 只有两个
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
build-plugins 里面有个 quarkus 插件用于编译、启动调式 quarkus 应用
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
profiles 里面有个 native profile 帮助生成 native 程序 (就是可以直接运行的文件,不需要 java)
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
程序已经自动生成了一个 helloworld
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 RESTEasy";
}
}
自动生成的文件包括
quarkus-getting-started/
├── pom.xml
├── README.md
└── src
├── main
│ ├── docker
│ │ ├── Dockerfile.jvm
│ │ ├── Dockerfile.legacy-jar
│ │ ├── Dockerfile.native
│ │ └── Dockerfile.native-distroless
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── demo
│ │ └── resources
│ │ └── GreetingResource.java
│ └── resources
│ ├── application.properties
│ └── META-INF
│ └── resources
│ └── index.html
└── test
└── java
└── com
└── example
└── demo
└── resources
├── GreetingResourceTest.java
└── NativeGreetingResourceIT.java
可以看到还包括 dockerfile、测试文件等
启动调式代码
可以看到 quarkus 是没有 main application 启动类的
可以通过命令启动
mvn compile quarkus:dev
如果用 IDEA 可以到右侧的 Maven 窗口找到对应项目下的 Plugins -> quarkus -> quarkus:dev 然后双击
(有时可能需要先点击 Plugins -> compiler -> compiler:compile)
访问 localhost:8080/hello 成功返回
Hello RESTEasy
尝试修改代码
@Path("/api/v1")
public class GreetingResource {
@Path("hello")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String hello() {
return "Hello Quarkus";
}
}
访问 http://localhost:8080/api/v1/hello 成功返回
Hello Quarkus
从日志可以看到当重新访问时 quarkus 会检测到文件的改变并重启
Restarting quarkus due to changes in GreetingResource.class.
所以 debug 的时候可以不用手动重启程序,只要刷新页面,或重新访问,就可以
修改配置文件同样不需要手动重启
# application.properties
quarkus.http.port=8180
greeting.message=hello world
// GreetingResource.java
@Path("/api/v1")
public class GreetingResource {
@ConfigProperty(name = "greeting.message")
String message;
@Path("hello")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String hello() {
return message;
}
}
刷新页面成功返回
hello world
从日志可以看到
File change detected: C:\Users\ezehlin\Desktop\IDEA_Project\quarkus-getting-started\src\main\resources\application.properties
Restarting quarkus due to changes in application.properties, GreetingResource.class.
但是端口的改变并没有起作用,需要重新启动才会改成 8180,不然还是用的 8080
如果要使用 yaml 配置文件需要添加依赖
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-config-yaml</artifactId>
</dependency>
application.yaml 和 application.propertis 可以同时存在,而且如果有相同配置项的话,是以 yaml 的为准
Quarkus 的所有可用配置项可以参考官网 https://quarkus.io/guides/all-config
测试
pom 文件里用于测试的两个依赖
<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>
一个用于启动 Quarkus 测试,一个用于测试 REST 请求 (也可以用其他 REST 测试工具)
为了和 junit5 配合,build-plugins 需要引入
<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>
测试类
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
@QuarkusTest
public class GreetingResourceTest {
@Test
public void testHelloEndpoint() {
given()
.when().get("/api/v1/hello")
.then()
.statusCode(200)
.body(is("Hello Quarkus"));
}
}
@QuarkusTest 会启动 Quarkus 应用,这里应该是 FT/IT 而不是 UT,做 UT 测试使用 mockito 就可以
测试时 server 和 client 都默认使用端口 8081
如果 test 目录下没有 resources 会使用 main 目录下的 resources
打包运行:JVM
执行 mvn package 或在 IDEA 的右边的 Maven 窗口双击 Lifecycle -> package
target 下会生成 quarkus-getting-started-1.0.0-SNAPSHOT.jar 包,但这不是可以直接运行的
target 下还生成了 quarkus-app 目录,这个目录下面有
app/
lib/
quarkus/
quarkus-app-dependencies.txt
quarkus-run.jar
然后可以通过 java 命令启动 quarkus 应用
java -jar target/quarkus-app/quarkus-run.jar
如果要在其他地方使用,需要把整个 quarkus-app 目录考过去
打包运行:Legacy-Jar
执行 mvn package 的时候添加参数指定 package 类型
mvn package -Dquarkus.package.type=legacy-jar
和 JVM 类型比,target 目录下没有了 quarkus-app 目录
而多了一个 quarkus-getting-started-1.0.0-SNAPSHOT-runner.jar
通过 java 命令启动
java -jar ./quarkus-getting-started-1.0.0-SNAPSHOT-runner.jar
如果要在其他地方使用,除了这个 jar 包,还需要把 target/lib 目录拷过去
打包运行:Native
执行 mvn package 的时候添加参数指定 package 类型
mvn package -Dquarkus.package.type=native
或是指定 pom 文件定义好的 profile
mvn package -Pnative
可以看到 target 目录下生成了一个 quarkus-getting-started-1.0.0-SNAPSHOT-runner 文件
可以直接运行这个命令启动
./quarkus-getting-started-1.0.0-SNAPSHOT-runner
这种模式不需要安装 java 环境
如果要在其他地方使用,只需要拷贝这个文件过去就可以
编译 native 模式需要安装 graalvm
wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.0.0.2/graalvm-ce-java11-linux-amd64-22.0.0.2.tar.gz
export GRAALVM_HOME=/home/lin/quarkus/graalvm-ce-java11-22.0.0.2
${GRAALVM_HOME}/bin/gu install native-image
如果不想安装 graalvm 可以通过 Docker 编译
mvn package -Pnative -Dquarkus.native.container-build=true
这样会启动一个有 graalvm 环境的容器,然后在里面做编译,编译结果会放到容器外面
(似乎可以不用指定,系统如果检查不到 graalvm 环境就会自动使用容器编译)
编译测试 native 模式感觉花的时间多了很多
Dockerfile
生成的项目里面有 4 个 Dockerfile
Dockerfile.jvm
Dockerfile.legacy-jar
Dockerfile.native
Dockerfile.native-distroless
分别对应前面提到的几种打包运行机制
native-distroless 和 native 只是基础镜像不同
distroless 镜像只包含应用程序以及其运行时所需要的依赖。不包含包管理器、shells 或者其他程序
官网说法: Distroless image support is experimental
测试: native
pom 文件的 native profile 下有 plugin
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<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>
native.image.path 指定了 native-image 的路径,就是上面生成的 quarkus-getting-started-1.0.0-SNAPSHOT-runner 路径
测试代码
import io.quarkus.test.junit.NativeImageTest;
@NativeImageTest
public class NativeGreetingResourceIT extends GreetingResourceTest {
// Execute the same tests but in native mode.
}
可以看到和前面的 GreetingResourceTest 是一样的,只是换成了 @NativeImageTest 注解
执行命令验证 mvn verify -Pnative
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.demo.resources.NativeGreetingResourceIT
Executing "/home/lin/quarkus/quarkus-getting-started/target/quarkus-getting-started-1.0.0-SNAPSHOT-runner -Dquarkus.http.port=8081 -Dquarkus.http.ssl-port=8444 -Dtest.url=http://localhost:8081 -Dquarkus.log.file.path=/home/lin/quarkus/quarkus-getting-started/target/quarkus.log -Dquarkus.log.file.enable=true"
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2022-01-29 17:34:27,262 INFO [io.quarkus] (main) quarkus-getting-started 1.0.0-SNAPSHOT native (powered by Quarkus 2.6.3.Final) started in 0.030s. Listening on: http://0.0.0.0:8081
2022-01-29 17:34:27,262 INFO [io.quarkus] (main) Profile prod activated.
2022-01-29 17:34:27,262 INFO [io.quarkus] (main) Installed features: [cdi, resteasy, smallrye-context-propagation, vertx]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.108 s - in com.example.demo.resources.NativeGreetingResourceIT
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
会启动 native.image.path 指定的可执行文件,然后做测试
同样需要有 graalvm 环境或是通过 docker build (哪怕 runner 已经存在也会执行)
如果希望直接测试已经存的 runner 而不重新 build,可以执行
mvn test-compile failsafe:integration-test
如果需要重新编译的话,那么还是比较花时间的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2021-01-16 5G 网络与物联网