Quarkus初探
- 背景
- 安装Quarkus
-
- [安装Quarkus CLI](#安装Quarkus CLI)
- 创建Quarkus项目
-
- 运行Quarkus
- 初探代码
- 数据持久化
- [Dev Service](#Dev Service)
- 构建native应用(依赖Graalvm)
背景
最早是在Infoq上了解到Quarkus这个框架,内部集成了Vertx,对native-image有着非常好的支持,启动速度非常快,占用内存很小(相较于Springboot),最近有时间就来体验一下。
安装Quarkus
Quarkus也是基于Java的微服务框架,所以想要体验Quarkus需要满足以下条件:
- JDK 17+,配置配置好了JAVA_HOME
- Apache Maven 3.9.6
- docker或podman(可选)
- Quarkus CLI(可选)
- Graalvm(可选,用于构建Native应用)
安装Quarkus CLI
Quarkus CLI中提供了很多命令,可以帮助我们快速的创建、运行Quarkus项目。这两天体验下来Quarkus CLI应该是主要对mvn的各类命令进行了集成,Quarkus CLI的命令相较于mvn来说更加便捷,也是推荐大家安装体验一下。目前官方支持以下几种安装方式:
- JBang - for Linux, macOS and Windows
- SDKMAN! - for Linux and macOS
- Homebrew - for Linux and macOS
- Chocolatey - for Windows
- Scoop - for Windows
我目前这台机器是通过SDKMAN来管理JDK版本的,这里选择了使用SDKMAN进行安装
sdk install quarkus
安装完成之后验证一下
quarkus --version
3.9.2
创建Quarkus项目
通过quarkus执行以下命令
shell
quarkus create app org.acme:getting-started-dev-services \
--extension='rest'
cd getting-started-dev-services
或者通过maven
shell
mvn io.quarkus.platform:quarkus-maven-plugin:3.9.2:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=getting-started-dev-services \
-Dextensions='rest'
cd getting-started-dev-services
如果想要创建一个gradle项目,可以追加以下参数 -DbuildTool=gradle 或 -DbuildTool=gradle-kotlin-dsl
对于使用windows的同学,有以下两点需要注意:
- 如果使用cmd,不要使用
\
分割命令,所有命令都要放在同一行中 - 如果使用PowerShell,-D的参数需要使用双引号括起来,e.g.
"-DprojectArtifactId=getting-started-dev-services"
运行Quarkus
shell
quarkus dev
or
./mvnw quarkus:dev
or
./gradlew --console=plain quarkusDev
正常启动后可以看到如下信息:
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2024-04-10 11:22:05,350 INFO [io.quarkus] (Quarkus Main Thread) code-with-quarkus 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.9.2) started in 0.842s. Listening on: http://localhost:8080
2024-04-10 11:22:05,351 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
然后我们在浏览器里访问 http://localhost:8080/hello,应该可以看到如下信息
Hello from Quarkus REST
初探代码
我们使用IDE打开刚刚创建的项目,可以看到只有一个很简单的类GreetingResource
java
package org.acme;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello from Quarkus REST";
}
}
相交于Springboot来说,这里不需要定义一个Application启动类,再增加一个Controller,一个简简单单的GreetingResource就完成了所有的功能,这里使用到的注解也都是jakarta
的标准注解。
修改一下代码
java
public String hello(@RestQuery String name) {
return "Hello " + name;
}
然后我们访问http://localhost:8080/hello?name=Bloom
这时我们会看到这样一条消息: Hello Bloom.
quarkus dev模式支持代码热部署,在我们修改代码之后无需重启,调试修改的时候会非常方便。
对应的我们修改一下单元测试 src/test/java/org/acme/GreetingResourceTest.java
,将
.body(is("Hello from Quarkus REST"));
修改为
.body(containsString("Hello"));
不然下次启动会报错(quarkus默认启动时会先执行单元测试)
数据持久化
在进行这一步之前,建议把我们本地的容器服务启动(docker或podman)起来,看看接下来会发生什么
创建PanacheEntiry
quarkus extension add hibernate-orm-panache jdbc-postgresql
或者
./mvnw quarkus:add-extension -Dextensions='hibernate-orm-panache jdbc-postgresql'
官方示例提供的是postgresql,你也可以替换为mysql。
创建一个Greeting.java
java
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.Entity;
@Entity
public class Greeting extends PanacheEntity {
public String name;
}
Panache
是一个基于Hibernate的orm框架,PanacheEntity
(类似于Spring-Data中的JpaRepository)中提供了大量的方法方便我们进行CRUD的操作
写入数据
java
@GET
@Transactional
@Produces(MediaType.TEXT_PLAIN)
public String hello(@QueryParam("name") String name) {
Greeting greeting = new Greeting();
greeting.name = name;
greeting.persist();
return "Hello " + name;
}
这里只是一个示例,在正式的代码中,GET
请求不应该向后端服务写入数据
读取数据
java
@GET
@Path("names")
@Produces(MediaType.TEXT_PLAIN)
public String names() {
List<Greeting> greetings = Greeting.listAll();
String names = greetings.stream().map(g-> g.name)
.collect(Collectors.joining (", "));
return "I've said hello to " + names;
}
来试一下效果, 先访问 http://localhost:8080/hello?name=Bloom, 再访问 http://localhost:8080/hello/names.
我们就能看到: "I've said hello to Bloom".
Dev Service
进行到这里,你肯定会有很多疑问,我本地都没有PG数据库,项目中也没有任何数据库相关的配置,为什么没有报错?这一切都是怎么发生的?
这时你可以打开自己的Docker desktop或者使用docker ps的命令,你就会发现有一个PG的数据库实例正在运行
% docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
38c07eab6549 postgres:14 "docker-entrypoint.s..." About a minute ago Up About a minute 0.0.0.0:61089->5432/tcp kind_yalow
这一切都是Dev Service
为我们做的,由于我们添加了jdbc-postgresql
的依赖,Dev Service就为我们启动了一个postgresql的容器,方便我们更加快速的进行项目搭建。
另外,在dev模式下,处于Dev Services管理的数据库,Quarkus会使用drop-and-create
的模式,所以我们每次修改代码重启服务之后,之前保存的数据都会丢失。在某些情况下,这种模式会很不方便,Quarkus也提供了一种机制来进行数据的初始化
我们可以创建一个sql文件src/main/resources/import.sql
sql
INSERT INTO Greeting(id, name)
VALUES (nextval('Greeting_SEQ'), 'Alice');
INSERT INTO Greeting(id, name)
VALUES (nextval('Greeting_SEQ'), 'Bob');
这样每次quarkus启动时都会将这些数据初始化进去
使用外部数据库
如果我们已经搭建好了一个数据库,我们可以在src/main/resources/application.properties
中增加如下配置
# configure your datasource
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = leopold
quarkus.datasource.password = bloom
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/mydatabase
区分dev和prod
如果你想在开发和测试环境中使用Dev Service,在生产环境使用外包数据库,可以通过一个%prod
的前缀来区分
# configure your datasource
%prod.quarkus.datasource.db-kind = postgresql
%prod.quarkus.datasource.username = leopold
%prod.quarkus.datasource.password = bloom
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/mydatabase
构建native应用(依赖Graalvm)
Quarkus一大亮点是对native-image有着非常好的支持,想要体验native-image需要先安装Graalvm,你可以通过官网或者SDKMAN进行安装和配置。
quarkus build --native
./mvnw install -Dnative
编译的过程会比较长,编译完成之后会在target文件夹下生成一个getting-started-dev-services-1.0.0-SNAPSHOT-runner的可执行文件,可以直接启动。
需要注意的是,通过native构建出来的可执行程序会失去跨平台的特性,因此在一个平台上构建出来的native-image往往无法在其他平台运行,最好的方式是通过docker等容器进行部署。
目前Graalvm有一个实验性的功能,在Linux平台来构建跨平台的应用,具体可以参考