f10@t's blog

Quarkus学习(一)概述以及编写REST服务

字数统计: 2.5k阅读时长: 10 min
2021/08/22

Quarkus -- A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards.

该框架定义为Kubernetes原生框架,可以很好的解决Serverless架构下资源占用大、冷启动慢等问题,实际效果还是挺惊艳的。这一篇开始学习Quarkus框架,本篇内容如下:

  • 介绍基本概念
  • 学习写简单的RESTful项目
  • 学习构建可执行文件以及构建Docker镜像

Quarkus介绍

什么是Quarkus?

官方对Quarkus的介绍是A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards.

所以我们可以管窥它的特点:

  • Kubernetes原生,即对容器化技术友好
  • 底层虚拟机可以使用HotSpot或者GraalVM
  • 集成了现存的很多优秀的Java库和标准

为什么要用Quarkus?

根据2020年NewRelic调查,在云服务领域,Java只占了6%的使用,具体如图:(图片来源:A guide to Java serverless functions | Opensource.com)

那么造成这个的原因是什么?很难在如Kubernetes的容器平台中优化Java的应用程序,我们可以使用NodeJs、Go语言进行一个对比,看看同样资源下支持的应用程序数目:

由于需要给每一个JVM堆分配内存,导致了Java应用程序较高的内存开销,相比与其他语言,尤其是内存开极小的GO,那基本上没得看,所以Go在云服务领域很流行。

为什么不继续用Spring?

上一节的原因导致了直接上Spring不是很合适,虽然,Spring Function支持使用java.util.function包来编写反应式函数,Spring也支持你把应用程序部署到Serverless平台上去,比如Kubeless、Apache OpenWhisk、Fission、Project Riff等。但是却也造成了包括上一节的问题:

  • 较大的内存开销
  • 冷启动慢
  • 响应时间缓慢

上述的问题如果放到不是Serverless的平台上,比如Kubernetes上,只会更严重。而对于Quarkus框架,官方宣称的开销如下:

可以看到不管是搭配HotSpot或者GraalVM,这个开销都要小得多得多。尤其是搭配GraalVM,它和HotSpot一样,属于JVM的一种,典型的GraalVM就是graalvm/graalvm-ce-builds,或者graalvm/mandrel (github.com)

常用注解以及RESTful程序编写

生成项目

官方项目生成网页

类似start.spring.io一样,Quarkus官方也有类似的网页,地址:Quarkus - Start coding with code.quarkus.io

比如在RESTful项目中,最基本需要两个依赖库:

直接下载到本地就可以使用了。

IDEA生成项目

在Ultimate版本中继承了Jetbrains闭源的插件,直接从这里进去就可以了,和Spring那个一样一样的。

quarkus中最基本的rest服务需要JAX-RS这个组件,关于json的插件你可以用自己喜欢的,比如Jackson或者JSON-B,这里用Jackson。

安利一个工具:Quarkus-cli

在官方的教程里,每次新建一个项目都需要敲很多的参数,个人觉得有点麻烦,好比这样的:

1
2
3
4
5
6
mvn io.quarkus:quarkus-maven-plugin:2.1.3.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=rest-json-quickstart \
-DclassName="org.acme.rest.json.FruitResource" \
-Dpath="/fruits" \
-Dextensions="resteasy,resteasy-jackson"

实际上在官方的页面里有推荐这么一个官方的小工具,可以帮助你新建一个项目,其实和Go语言里的Beego中的bee工具作用差不多:

只不过这玩意儿貌似还在改进中,但是实测可以用。安装方法如下:

1
2
3
4
5
6
7
# 先安装JBang,这个小玩意儿还挺有意思的,可以看看:https://www.jbang.dev/
iex "& { $(iwr https://ps.jbang.dev) } app setup"

# 然后我推荐你先去阿里云仓库下载quarkus-cli的runner包,然后再安装.
# https://developer.aliyun.com/mvn/search 搜索quarkus-cli,然后找到quarkus-cli-2.1.0.Final-runner.jar就行,这是最新的
# 然后jbang命令安装,这里我取名为quarkus。建议把jar包放到本地maven仓库的io.quarkus.quarkus-cli文件夹下
jbang app install --force --fresh --name quarkus <你的jar包的位置>

安装后就可以正常用了:

默认的create不带子命令就是在创建应用,Quarkus-cli创建项目时候有一个非常Nice的参数:--dry-run,对没错就是k8s那个dry-run,你可以预览并检查生成的选项而不是直接生成项目。好比这样:

我只能说好用的一批。好比我们创建一个rest-demo项目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
quarkus create cn.edu.xidian:quarkus-rest-demo -P 2.1.3.Final
# 输出:
# Creating an app (default project type, see --help).
# -----------
#
# applying codestarts...
# >> java
# >> maven
# >> quarkus
# >> config-properties
# >> dockerfiles
# >> maven-wrapper
# >> resteasy-codestart
#
# -----------
# [SUCCESS] quarkus project has been successfully generated in:
# --> C:\Users\22629\IdeaProjects\quarkus-rest-demo
# -----------
# Navigate into this directory and get started: quarkus dev

项目结构如下,可以对比Spring发现多了docker的文件夹,其他没啥区别。

编写结束后如果要启动项目只需要在项目文件夹下执行quarkus dev就可以了,也不需要mvn compile quarkus:dev了,这个工具还是很方便的。

常用注解

下面小结一下在Quarkus框架中常用的注解,这里主要是编写Restful服务常用的。Quarkus的ioc和rest规范都是跟从的官方,即:

  • ioc使用的是JSR 299和JSR 330
  • rest使用的是JAX-RS

所以其实很多注解都是从上面这些依赖中了来的。下表是我自己总结的常用的Quarkus中的注解,后续我会继续添加这个表

注解名称 注解含义
Rest类: javax.wx.rs包
@Path 指明web的请求路径,比如@Path('/book/{name}'),类似Spring的@RequestMapping
@PathParameter 配合@Path注解获取GET参数,比如上面那个的获取方法:public void func (@PathParameter('name') String name) {}
@Produces 指明这个类所有函数/单个函数返回的参数类型,比如REST项目@Produces(MediaType.APPLICATION_JSON)
@Consumes 指明这个函数所有函数/单个函数接收的参数类型,比如REST项目@Consumes(MediaType.APPLICATION_JSON)
@GET 声明GET方法,类似Spring的@GetMapping
@POST 声明POST方法,类似Spring的@PostMapping
@DELETE 声明DELETE方法
@PUT 声明PUT方法
Bean相关类
@Injection 声明一个可以被注入的类、方法、变量,类似@Autowired
@Singleton 同@Inject,但是同时他也是个单例,类似Spring的@Component
@ApplicationScoped 声明一个全局变量的bean
配置类
@ConfigProperty 从配置文件(application.properties)中读取变量,比如@ConfigProperty(name="vartest", default="test"),即从配置文件中读取'vartest'变量的值,没有的话就是用default值

一个RESTful程序示例

下面写一个简单的图书restful服务。

Spring中我们将业务控制器分开,xxxController这种,Quarkus中喜欢把这个叫Resource,

我们先定义一个实体Book,这里注意,我们将使用Jackson来对对象进行序列化处理,所以一定要设置Getter,不然会获取不到属性,你也可以用@JsonIgnore注解来屏蔽一些属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.util.Objects;

/**
* @author lzwgiter
* @since 2021/08/22
*/
public class Book {
/**
* 索书号
*/
private String indexId;

/**
* 书本名称
*/
private String name;

/**
* 作者信息
*/
private String author;

public Book() {
}

public Book(String name, String indexId, String author) {
this.name = "《" + name + "》";
this.indexId = indexId;
this.author = author;
}

public String getIndexId() {
return indexId;
}

public String getName() {
return name;
}

public String getAuthor() {
return author;
}
}

然后我们定义对应的Resource,可以看到所有与RESTful相关的注解,都来自javax.ws.rx包,都是JAX-RS规范。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.DELETE;
import java.util.List;
import java.util.LinkedList;

/**
* @author lzwgiter
* @since 2021/08/22
*/
@Path("/book")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class BookResource {

private final List<Book> bookList = new LinkedList<>();

public BookResource() {
this.bookList.add(new Book("书_1", "abcdefg1", "lzwgiter"));
this.bookList.add(new Book("书_2", "gfseffda", "lzwgiter"));
}

@GET
public List<Book> getAllBooks() {
return this.bookList;
}

@POST
public List<Book> addBook(Book book) {
this.bookList.add(book);
return this.bookList;
}

@DELETE
public List<Book> delBook(Book book) {
this.bookList.removeIf(existBook -> existBook.equals(book));
return this.bookList;
}

}

代码编写后,我们quarkus dev让他运行:

然后就可以请求服务了:

构建可执行文件

接下来的两小节包括构建可执行文件和Docker镜像,这里不推荐使用那个quarkus-cli,它不支持打包到docker,建议用mvn命令。

这里我是在Linux下构建的可执行文件,windows下我会报只能使用AMD平台的错误,而且即使是在x64 Native Tools Command Prompt for VS 2019下不能解决。

构建直接使用命令mvn package -Pnative

我虽然是在虚拟机里构建的,但是这个速度也有点慢了吧。。构建完成后会生成一个xx-runner的文件:

然后我们直接./xxx-runner启动就可以了,我这里用的是官方基于Oracle GraavlVM定制的一个下游版本,叫Mandrel,这时候你就发现牛逼的地方了:

好家伙,22ms就起来了,我直呼image-20210823165047294

当然运行结果是一样的。

构建Docker镜像

这里只介绍最简单的办法,前面我们已经通过mvn package -Pnative命令完成构建了,此时在你的src/main/docker目录里可以看到一个Dockerfile.native,内容如下,这个不需要你写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode
#
# Before building the container image run:
#
# ./mvnw package -Pnative
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.native -t quarkus/quarkus-rest-demo .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/quarkus-rest-demo
#
###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4
WORKDIR /work/
RUN chown 1001 /work \
&& chmod "g+rwX" /work \
&& chown 1001:root /work
COPY --chown=1001:root target/*-runner /work/application

EXPOSE 8080
USER 1001

CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

然后我们按照说明去Docker中构建镜像:

docker build -f src/main/docker/Dockerfile.native -t quarkus/quarkus-rest-demo .

到这里镜像就构建好了,就可以投入使用了:

参考学习

CATALOG
  1. 1. Quarkus介绍
    1. 1.1. 什么是Quarkus?
    2. 1.2. 为什么要用Quarkus?
    3. 1.3. 为什么不继续用Spring?
  2. 2. 常用注解以及RESTful程序编写
    1. 2.1. 生成项目
      1. 2.1.1. 官方项目生成网页
      2. 2.1.2. IDEA生成项目
      3. 2.1.3. 安利一个工具:Quarkus-cli
    2. 2.2. 常用注解
    3. 2.3. 一个RESTful程序示例
    4. 2.4. 构建可执行文件
    5. 2.5. 构建Docker镜像
  3. 3. 参考学习