Quarkus上的Spring Boot:Magic or Madness?


Quarkus是专为OpenJDK HotSpot(或zSeries上的OpenJ9)和GraalVM量身定制的Java堆栈,它是从优化的Java库和标准中精制而成的。对于构建高度可扩展的应用程序,同时使用比其他Java框架更少的CPU和内存资源,它是一个不错的选择。这些应用程序可以是传统的Web应用程序,无服务器应用程序,甚至可以充当服务。

有许多记录在案的组织将其应用程序迁移到Quarkus的实例。在本文中,让我们看一下从Spring Boot到Quarkus的这种迁移路径,这既是魔术又是疯狂!魔术将是挥舞双手并执行迁移而无需更改一行代码。疯狂将试图弄清楚它是如何完成的。

应用程序

该应用程序是一个简单的“待办事项”任务管理系统。用户可以输入待办事项,然后在完成后将其选中。这些项目存储在PostgreSQL数据库中。您可以在此处找到所有应用程序的源代码。有一个版本使用Gradle而不是Maven作为gradle分支上的构建工具。

启动数据库

该应用程序需要一个PostgreSQL数据库,所以我们要做的第一件事是使用Docker或Podman在本地启动实例:

docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name tododb -e POSTGRES_USER=todo -e POSTGRES_PASSWORD=todo -e POSTGRES_DB=tododb -p 5432:5432 postgres:11.5

端口5432上的PostgreSQL 11.5实例现在应该正在运行。应该创建tododb用户todo可以使用密码访问的模式todo。

运行应用程序

发出命令运行应用程序./mvnw clean spring-boot:run。如果要使用Gradle而不是Maven,请首先切换到gradle分支(git checkout gradle),然后运行命令./gradlew clean bootRun

您应该看到标准的Spring Boot标语:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.1)
INFO 68107 --- [  restartedMain] i.q.todospringquarkus.TodoApplication    : Started TodoApplication in 4.074 seconds (JVM running for 4.645)

注意启动时间。我们将再讨论一下。

应用程序运行后,在您喜欢的浏览器中导航到http:// localhost:8080。您应该看到如图1所示的主应用程序屏幕。

“我的第一个待办事项”标记为已完成的Spring Todos应用程序。

1-maintodos.png

试一下该应用程序。在文本框中输入新的待办事项,然后按Enter。该待办事项将显示在列表中,如图2所示。

带有新待办事项的Spring Todos已添加到列表中。

2-todoadded.png

  1. 单击待办事项旁边的空白圆圈以完成操作,或取消选中它以将其标记为未完成。
  2. 单击X删除待办事项。
  3. 页面底部的OpenAPI链接将打开该应用程序的OpenAPI 3.0规范。
  4. Swagger UI链接打开嵌入式Swagger UI,它可以被用来执行一些的REST风格的端点直接。
  5. 该Prometheus Metrics 链接指向普罗米修斯指标终点,这将间歇刮普罗米修斯。
  6. 在健康检查链接打开内置的健康检查由Spring启动曝光。 继续尝试一下,看看一切都在起作用。完成后,别忘了回到这里!完成后,使用CTRL-C键盘上的停止应用程序。

检查内部

该应用程序是具有以下功能的全功能Spring Boot应用程序:

  • 用于构建REST层的Spring MVC:
    • 打开src/main/java/io/quarkus/todospringquarkus/TodoController.java以查找Spring MVC RESTful控制器,公开用户界面可用的各种端点。 Spring Data JPA用于定义关系实体以及存储和检索它们:
    • 打开src/main/java/io/quarkus/todospringquarkus/TodoEntity.java以找到Java Persistence API(JPA)实体,该实体代表用于存储待办事项的关系表。
    • 打开src/main/java/io/quarkus/todospringquarkus/TodoRepository.java以查找Spring Data JPA信息库,公开TodoEntity的所有创建,读取,更新和删除操作。
  • Spring Boot执行器用于提供操作能力,包括运行状况检查和指标收集。
  • SpringDoc OpenAPI 3,用于生成和公开RESTful API信息以及嵌入式Swagger UI端点。
  • Prometheus千分尺注册表,用于向Prometheus公开指标。
    • 打开src/main/resources/META-INF/resources以查找所使用的用户界面组件。

Configuration

打开src/main/resources/application.properties以找到应用程序配置:

spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.url=jdbc:postgresql://localhost:5432/tododb
spring.datasource.username=todo
spring.datasource.password=todo
springdoc.api-docs.path=/openapi
springdoc.swagger-ui.path=/swagger-ui
management.endpoints.web.exposure.include=prometheus,health

打开src/main/resources/import.sql以查找一些SQL,这些SQL将使用初始数据集预填充数据库表:

INSERT INTO todo(id, title, completed) VALUES (0, 'My first todo', 'true');

而已!对于功能齐全的应用程序,这里没有太多的代码,比“ Hello World!”要多得多。

The Magic

选择了此迁移的一个硬性要求:不能以任何方式修改应用程序的源代码。

您准备好魔术了吗?返回命令行并运行命令./.mvnw clean spring-boot:run。如果要使用Gradle而不是Maven,请首先切换到gradle分支(git checkout gradle),然后运行命令./.gradlew clean bootRun

您会注意到的第一件事是Quarkus标语和启动消息,而不是Spring Boot:

__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
INFO  [io.quarkus] (Quarkus Main Thread) todo-spring-quarkus 1.0.0-SNAPSHOT on JVM (powered by Quarkus 1.10.5.Final) started in 2.743s. Listening on: http://localhost:8080
INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-postgresql, kubernetes, micrometer, mutiny, narayana-jta, resteasy, resteasy-jackson, smallrye-context-propagation, smallrye-health, smallrye-openapi, spring-boot-properties, spring-data-jpa, spring-di, spring-web, swagger-ui]

等等,发生了什么事?该应用程序现在是Quarkus应用程序,不再是Spring Boot应用程序?太疯狂了!

要证明吗?返回浏览器窗口(如果您将其关闭,请访问http:// localhost:8080)并重新加载页面。相同的用户界面在那里,并且功能齐全。单击页面底部的所有各种链接。它们都像以前一样完全起作用。

另外,请注意启动时间。给定完全相同的代码库(在所示示例中,Spring为4.074s,而Quarkus为2.743s),Quarkus版本的启动时间几乎减少了一半。那就是超音速,亚原子,Java!

什么都没有改变,那么这一切是怎么发生的呢?一个好的魔术师不会透露他或她的秘密!

The Madness

所有优秀的魔术师用 sleight-of-hand表演一招时,分散他的或她的听众。这篇文章中还没有显示的一些东西是肉眼看不见的。您有发现可疑之处吗?让我们仔细看看该技巧是如何完成的。

Execution

仔细查看用于运行应用程序的命令:使用Maven:

  • Spring Boot:./mvnw clean spring-boot:run
  • Quarkus: ./.mvnw clean spring-boot:run

使用Gradle:

  • Spring Boot:./gradlew clean bootRun
  • Quarkus:./.gradlew clean bootRun

注意到有什么区别吗?在Quarkus版本中使用了另一个可执行文件(.mvnw/ .gradlew)。如果打开这些文件并仔细检查它们,您会发现一些棘手的事情:

.mvnw

#!/bin/sh
./mvnw clean quarkus:dev -Pquarkus

.gradlew

#!/bin/sh
2
./gradlew -Pprofile=quarkus $@

我们使用了一些技巧来掩盖用于启动应用程序的实际命令。稍后更多关于Gradle和Maven配置文件的信息。

配置

您是说Quarkus知道如何阅读和理解其中的Spring Boot配置src/main/resources/application.properties吗?好吧,不。记住,这是一个魔术。魔术师从不说完整的事实。上面的示例中未显示的是特定于Quarkus的配置。它被隐藏在文件的更下方。

如果您重新打开src/main/resources/application.properties并一直滚动到底部(在第58行附近),则会看到一些其他配置:

quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/tododb
quarkus.datasource.username=todo
quarkus.datasource.password=todo
quarkus.datasource.metrics.enabled=true
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.sql-load-script=import.sql
quarkus.micrometer.export.prometheus.path=/actuator/prometheus

quarkus.smallrye-health.root-path=/actuator/health

此配置类似于文件顶部的Spring Boot配置。但是,它所做的一件事是为Prometheus和运行状况探针端点重新定义路径,以便它们与Spring Boot执行器端点的路径匹配。这使屏幕底部的Prometheus度量标准和运行状况检查链接可以正常工作,而无需更改用户界面。

依存关系

可以想象,有许多依赖项需要更改,添加或更新。

简单地将一些Spring Boot依赖项交换为Quarkus依赖项将有很长的路要走。还有其他一些功能,例如Prometheus指标,OpenAPI文档,Swagger UI集成以及需要添加其他依赖项的运行状况检查。

现在您可能在想自己,“但是我什么都没做。我所做的只是运行Maven或Gradle命令,并且整个应用程序都以Quarkus应用程序而不是Spring Boot应用程序的身份运行。怎么可能?”

构建设置

构建文件是所有魔术发生的地方。但是,您使用的构建工具确定了依赖性解析魔术的实际发生方式。

Maven

在main分支上,打开pom.xml文件。你会立刻被构建文件分成多个Maven的配置文件,spring以及quarkus与spring轮廓是默认的配置文件。这些配置文件定义了在您运行./mvnw clean spring-boot:run或时./mvnw -Pquarkus clean quarkus:dev(其中使用上述.mvnw脚本中的某些重定向伪装而成)包含的依赖项和构建插件。

Gradle

另一方面,Gradle没有配置文件的概念。您会在项目分支中注意到一些.gradle文件gradle:

build-common.gradle

包含适用于两个版本的应用程序的任何通用构建逻辑,包括设置所生成工件的groupId / version以及工件存储库定义。

build-spring.gradle

包含特定于该应用程序的Spring版本的所有构建逻辑,插件和依赖项。

build-quarkus.gradle

包含特定于Quarkus版本的应用程序的所有构建逻辑,插件和依赖项。build-quarkus.gradle甚至介绍了Spring Boot的bootRun任务,quarkusDev并通过Quarkus Gradle插件将其重新映射到任务。

settings.gradle

魔术真正发生的地方!该settings.gradle文件查找名为的Gradle项目属性profile: 如果找不到此属性,则默认值为spring。 然后,它将项目的构建文件build-spring.gradle或设置build-quarkus.gradle为要使用的主要构建文件。 通常,如果您需要Gradle模拟Maven配置文件的功能,那么这是一个非常好的模式。

应用主类

在本文开头提到,我们希望执行迁移而无需更改任何代码。每个Spring Boot应用程序都需要有一个“应用程序”类,其中包含一个main方法,并带有注释@SpringBootApplication。在我们的项目中,src/main/java/io/quarkus/todospringquarkus/TodoApplication.java是该类。

Quarkus不需要这样的类,任何Quarkus Spring兼容性扩展都@SpringBootApplication不会为注释或SpringApplication此类中引用的类提供解析。

那么,有什么用呢?我们没有进行任何代码更改,但是这些类在Quarkus中似乎可以很好地解决。

您会在pom.xml(对于Maven)/ build-quarkus.gradle(对于Gradle)中都注意到一个特殊的注释,就在依赖项的依赖项声明的正上方org.springframework.boot:spring-boot-autoconfigure

This dependency is a hack for TodoApplication.java, which isn't required for Quarkus. Point of demo is to NOT have any code changes.

这是这一部分技巧的关键。这种依赖关系允许Spring Boot和Quarkus在构建时解析这些类。依赖optional在Maven / compileOnlyGradle中声明,这意味着它永远不会包含在Quarkus构建生成的应用程序二进制文件中。它会包含在Spring Boot构建所生成的二进制文件中,因为所有其他spring-boot-starter-*依赖项也都依赖于它,因此它是可传递的。

Wrap Up

您在本文中看到了如何获取现有的Spring Boot应用程序并在Quarkus上运行它,而无需对代码进行任何更改。方法是魔术还是疯狂?也许两者都有一点?由您自己决定。

这篇文章展示了一种将“ Hello World”以外的应用程序从Spring Boot迁移到Quarkus的方法,其硬性要求是不更改一行源代码。它根本不是要代表唯一的潜在迁移路径。此外,有时应用程序使用某些库或API时,可能没有Quarkus等效项。在这些情况下,可能需要更改代码或进行一些重构。

非常感谢Eric Murphy。他是该应用程序的原始作者,并提出了魔术技巧。

参考

这篇文章的所有源代码都可以在这里找到。该main分支包含Maven的版本和gradle分支包含的摇篮版本。两个分支上的应用程序源代码相同。


原文链接:http://codingdict.com/