骑 Camel Quarkus:轻松构建 API

Camel Quarkus

了解 Camel,Quarkus 的集成瑞士军刀。该示例鼓励 API 最佳实践,轻松的编码工作展示了 AtlasMap 的数据转换功能。

简介

Quarkus 提供了广泛的扩展集合,用于连接 Web、数据和消息系统,为开发人员提供了强大的功能。然而,在许多情况下,手头的问题已经属于一个(或多个)众所周知的 企业集成模式

开发人员通常在不知情的情况下启动他们的项目,而 Apache Camel 已经完善了解决集成模式的最佳方法。Camel Quarkus 提供了 数百种连接器 和丰富的数据中介功能:数据格式、转换器、模板、自定义处理器等。

Camel QuarkusApache Camel 社区的一个子项目,它使 Camel 能够在 Quarkus 上运行。Apache Camel,通常被称为集成领域的瑞士军刀,是旨在解决所有集成问题的最受欢迎的开源社区项目。


一个 OpenAPI 示例

让我们选择一个有用的示例,以突出 Camel Quarkus 相对于其他开发方法的优势。实现和演进 API 服务听起来是一个几乎所有读者都能产生共鸣的用例。我们将使用广泛采用的 OpenApi 规范。

Putting together APIs
图 1. 拼凑一个 OpenAPI 服务

当然,本文档中说明的示例是在集成环境中进行的,您需要启用访问或集成源系统,执行某种数据处理,并连接到后端终结点并发送数据。

总之,Camel Quarkus 并不是一个“包打一切”的解决方案。如果您的场景偏离了上述上下文,例如,具有大量数据存储交互的数据访问层、Web 服务器、媒体应用程序等,它将不适合。

我们发现许多 Quarkus(也包括非 Quarkus)的示例展示了如何定义和实现 API。它们都力求提供最大的帮助并提出前进的方向。我意识到这篇文章也不例外。然而,我确信 Camel Quarkus 提供了一种值得考虑的优雅且毫不费力的方案。

代码优先 vs 合约优先

尽管这是实现 API 的两种不同策略,各有优缺点,但我们坚持采用合约优先的方法,即在代码实现开始之前提供 API 规范(合约)。

在我们的示例中,开发团队不拥有 API。他们的任务是实现要公开的服务并遵守给定的 API 规范。组织中的另一个团队负责设计、发布和交付 API 治理。下图说明了合约优先的方法。

Contract-first approach
图 2. 合约优先的方法

代码优先策略意味着 API 规范源自已实现的 कोड。您可以使用库根据开发人员已编写的代码自动生成规范。代码优先更适用于快速原型设计,或者当您完全控制 API,并且对开发采取非常开放、宽松和灵活的方法,对他人影响很小或没有影响时。

Code-first approach
图 3. 代码优先的方法

Camel 和 REST 的基础知识

以下是对于不熟悉 Camel 和如何实现 REST API 的人员的快速摘要。

Camel 有其领域特定语言 (DSL) 来定义处理流程,称为 Camel DSL。您在 DSL 中使用 Camel 组件(又名连接器)将数据从源移动到目标。Camel Quarkus 提供了 300 多个可用扩展

Camel 为特定的 REST 实现提供了额外的领域特定语言:REST DSL。在 Camel 中实现 REST 服务时,您将两个 DSL 链接起来以定义服务的端到端行为。

Camel DSLs chained to process incoming requests
图 4. 链接的 Camel DSL 来处理传入的请求。

Camel 通过 Camel 的 'direct' 组件将来自 REST DSL 的传入 REST 请求转发到主 DSL,该组件本质上是一个用于内部调用的连接器,就像从一行 Java 代码调用 Java 方法一样。


轻松的准备工作

最终目标是让开发人员不必处理与 API 相关的准备和配置,让他们能够专注于业务逻辑。

具体来说,在之前的图表中,当开发人员采用代码优先的方法时,REST DSL 非常方便。然而,在合约优先的世界中,它的定义显得多余,因为提供的 OpenAPI 规范已经定义了所有的 API 细节。

Camel 允许您从 OpenAPI 规范自动生成 REST DSL。这种自动化简化了工作,让开发人员只需专注于处理流程的实现。

Automated REST DSL
图 5. 自动化的 REST DSL,手动编写的主 DSL。

上面显示的自动化代码生成是通过配置以下 Maven 插件启用的

<plugin>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-restdsl-openapi-plugin</artifactId>
    <executions>
        <execution>
            <id>simple</id>
            <goals>
                <goal>generate-xml</goal>
            </goals>
            <configuration>
                <specificationUri>src/main/resources/META-INF/openapi.json</specificationUri>
                <restConfiguration>false</restConfiguration>
                <outputDirectory>${project.build.directory}/classes/routes</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

请注意 `outputDirectory` 参数已设置为 target 目录内的目标。此设置允许 Quarkus 在启动时加载 Camel REST 资源,并防止源目录被不必要的代码污染。

另外,您会注意到我们是从项目的源目录中选择 OpenApi 规范的。最佳实践是通过另一个 Maven 插件从代表真实来源的服务注册中心获取 OpenAPI。

本文示例中使用的 OpenApi 定义是由 Apicurio Studio 创建的,这是一个漂亮且直观的设计时可视化工具。

让我们假装我们从远程服务器获取 OpenApi 定义。在此假设下,下图显示了项目如何保持简单和干净。Maven 在编译时将 OpenApi 和 REST 定义注入到 target 目录中。在打包解决方案时,它包含运行所需的所有必要工件。

Source is clean. Essential artefacts are generated and injected
图 6. 源是干净的。必要的工件被生成和注入

最佳实践是从远程 服务注册中心(真实来源)获取 OpenAPI 规范。为了简单起见,我们的项目已经包含了规范。


应用程序版本 1.0

让我们看看我们选择的(示例)API 服务,并了解如何在第一个迭代中驱动其实现。API 设计团队已发布了名为“Individual”的服务 OpenAPI 规范 v1.0,而我们,开发人员,需要根据其定义实现该服务。

该服务的第一个版本只有一个操作来检索个人的详细信息。通过设置我们的项目以解析和自动生成 REST DSL,如上一节所述,我们只需要实现一个 Camel Route 来处理传入的服务调用,如下所示

First version of the service to implement
图 7. 需要实现的第一个服务版本。

我们将保持用例相对简单;我们的重点仍然是“Camel Quarkus 的简单 API”。但尽管如此,我们希望场景仍然在集成上下文中,以展示一些 Camel Quarkus 的功能。

我们的 Camel 路由连接到一个允许访问“个人”数据的传统后端。处理逻辑需要将传入的 OpenApi 调用适配到传统后端系统的接口。这种数据适配需要在请求和响应流期间进行数据转换。

End to end processing flow
图 8. 端到端处理流程。

在经典的开发方法中,开发人员需要集成 Java 数据模型来操作和处理进出数据,例如,通过定义 Java 类和数据结构来表示服务需要操作的数据。

在我们的示例中,OpenApi 合约已经预定义了数据模型,描述了每个操作的输入和输出。同样,后端也定义了一个要遵守的合约(或接口)来触发调用。

使用 Apache Camel,我们可以通过使用专门用于数据转换的 Camel 功能来保持代码的整洁。在我们的示例中,我们将出于多种原因使用 AtlasMap 组件

  • 它提供了直观的可视化工具,用于将源数据映射到目标数据
  • 它在一个操作中执行结构和数据转换(JSON ⇄ XML)
  • 它不需要预定义 Java 数据模型
AtlasMap combines 2 actions in 1: structural and format transformations
图 9. AtlasMap 将 2 个操作合并为 1 个:结构和格式转换。

让我们开始看看开发人员需要生成的路由定义,以及 Camel 如何将 OpenAPI 操作链接到其代码实现

Camel uses the operationId as a link to invoke the Camel route
图 10. Camel 使用 `operationId` 作为调用 Camel 路由的链接。

Camel Quarkus 使用 OpenAPI 规范中的 `operationId` 来触发内部调用(使用 `direct` 组件),并期望一个具有匹配 `operationId` 值的 Camel 消费者(`from` 标签)来处理客户端请求。

Camel 路由再简单不过了

from("direct:getDetails")
.to("atlasmap:map/request.adm")
.to("direct:call-backend")
.to("atlasmap:map/response.adm");
  1. 第一行声明了路由(并匹配 `+operationId+`)。
  2. 第二行应用数据适配(或映射)来准备后端调用的有效载荷。
  3. 第三行调用一个 Camel 路由,该路由调用后端并收集响应。
  4. 第四行将响应 XML 映射为符合 OpenApi 定义的 JSON 数据。

您可以使用 AtlasMap 的 VSCode 扩展轻松创建数据映射定义。在 VSCode 中编辑 Camel 路由时,您会找到一个上下文相关的可操作提示,您可以单击它(在“atlasmap”代码行上),它将启动 AtlasMap 编辑器并加载数据映射定义。

VSCode prepends an action link to open the visual mapping editor
图 11. VSCode 前置一个操作链接以打开可视化映射编辑器。

Apache Camel 社区的博客 介绍了如何使用该工具。

下图显示了请求流的 AtlasMap 中的数据映射定义

Mapping definition for the request flow

属性 `id`(左侧)代表 HTTP URL 中的 `{id}` 路径参数。它映射到目标 XML 数据结构,并连接到 `id` 节点(右侧)。您可以通过从左到右拖放操作来定义映射。

在数据转换操作之后,流程调用后端。下面的代码片段显示了开发人员用于触发 HTTP 请求的 Camel 路由定义。

from("direct:call-backend")
.removeHeaders("*")
.setHeader(Exchange.HTTP_METHOD,  constant("POST"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/xml"))
.to("http:{{api.backend1.host}}/camel/individual/details");

此路由中的两个主要操作是

  1. 设置标头(确保我们清理传入的标头)
  2. 使用 Camel 的 HTTP 组件触发调用。

与传统后端系统预期的 XML 交互如下所示

Call from Camel to the backend
图 12. 从 Camel 到后端的调用。

响应数据映射定义将在 AtlasMap 中定义,如下所示

Mapping definition for the response flow

左侧的数据结构表示要映射的源 XML(后端响应),右侧的数据结构表示要组合并发送回客户端的目标 JSON 数据。

总结

让我们回顾一下开发人员在 Individual API 的第一个迭代中需要进行的实现工作

  1. 设置项目(包含依赖项、插件等)

  2. 定义 Camel 路由
    1. 主要的 Camel 路由
      (4 行代码)
    2. 后端调用路由
      (5 行代码)

  3. 定义数据转换
    1. 请求映射
    2. 响应映射

在我看来,这看起来不错。


应用程序版本 2.0

当开发团队收到设计团队发布的 Individual API 的新版本 2.0,并新增了一个要实现的操作时,这种开发方法的真正优势以及使用 Camel Quarkus 的简便性就更加明显了。

版本 1.0 仅用于内部消费。现在有需求将服务对外公开,这需要引入一定级别的数据保护,以确保客户数据得到保密。

版本 2.0 定义了一个开发人员需要实现的新操作。希望对现有代码的影响不会太大。

Second version of the service with a new operation
图 13. 具有新操作的第二个服务版本。

新操作的主要目标是匿名化 JSON 响应有效载荷中的敏感数据,以便发送回。除此之外,逻辑也适用,我们以相同的方式准备和调用后端以获取个人数据。

在版本 1.0 中,我们看到了使用 Java DSL 实现的路由。在版本 2.0 中,我们将使用 XML DSL。

使用一种 DSL 而非另一种更多是用户偏好。XML DSL 有助于保持 Camel 路由定义的良好组织。相比之下,Java DSL 位于 Java 类中,有时难以与常规 Java 代码一起找到。您的代码可能会变得有些混乱和无序。此外,Java DSL 不受图形工具的支持,而 XML 和 YAML 是支持可视化帮助的 DSL。

让我们看看开发人员在 XML 中编写的新 Camel 路由定义

<route>
    <from uri="direct:getAnonymousDetails"/>
    <to uri="atlasmap:map/request.adm"/>
    <to uri="direct:call-backend"/>
    <to uri="atlasmap:map/response-anonymous.adm"/>
</route>

新路由与第一个几乎相同。您只会注意到两个区别

  1. `'direct'` 组件匹配 2.0 版本中的新 `'operationId'`
  2. 响应数据映射是新的

2)中的数据映射定义在 AtlasMap 编辑器中如下所示

Mapping definition anonymised for the response flow

上面的主要亮点是定义了一个 `anonymous` 常量,该常量映射到 `fullName` 和 `passportId` 目标字段

  • 名称:anonymous
  • 值:*********

新的 Camel 路由和新的映射定义完成了 v2.0 中新操作的实现。此时,开发人员因巨大的工作量和花费了很长时间才交付新版本而疲惫不堪。

总结

让我们快速回顾一下完成服务第二次迭代所需的步骤

  1. 将 OpenApi v1.0 替换为新规范 v2.0

  2. 创建新的 Camel 路由
    (如果复制/粘贴,则为 1 行)

  3. 创建新的数据映射
    (如果复制/粘贴,则为 2 次拖放操作)

同样,这并不算太糟糕。工作量确实很小。


尝试操作

要在代码优先的开发中发现和探索服务,您需要确保项目从已实现的 कोड 自动生成规范。

在我们的情况下(合约优先),规范已经提供。我们可以使用 Quarkus 提供的 Smallrye OpenApi 扩展轻松地公开它并嵌入 Swagger-UI 客户端。确保您的 POM 文件包含以下依赖项

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>

有关如何公开 OpenApi 定义的更详细信息,您可以阅读 Quarkus 文档的以下部分。有不同的设置我们可以根据自己的喜好进行配置。我们已经配置了一些。如果您感到好奇,我们鼓励您查看文章随附的源代码。

我们可以使用以下命令在本地编译和运行我们的应用程序

./mvnw clean quarkus:dev

请记住,我们的 Camel 集成调用了传统后端。我们在 GitHub 项目中提供了一个。请确保您阅读并遵循 'Readme.md' 说明来准备存根(传统后端)。

一旦应用程序启动并运行,请打开浏览器,在地址栏中输入以下 URL 来发现服务

https://:8080/camel/openapi.json

点击并尝试 v1.0 操作

First operation in Swagger client

为 `{id}` 参数输入一个虚拟值,例如 `'123'`。如果您的传统系统(存根)正在运行,您应该会获得以下响应

{
  "passportId": "123456789-A",
  "fullName": "Some One",
  "addressLine1": "1 Some Street",
  "addressLine2": "Somewhere SOME C0D3",
  "addressLine3": "UK"
}

现在,尝试我们的 v.2.0 操作

Second operation in Swagger client

您应该会获得以下响应

{
  "addressLine1": "1 Some Street",
  "addressLine2": "Somewhere SOME C0D3",
  "addressLine3": "UK",
  "passportId": "**********",
  "fullName": "**********"
}

您会注意到,根据 AtlasMap 中的映射设置,某些字段现在已匿名化。

最后的话

本文向您展示了选择 Camel Quarkus 和合约优先的实现方法如何提供极大的简便性和低维护成本。它允许快速的功能增长。

当您的流程将数据从源传输到目标时,请使用 Camel Quarkus,这可能是构建集成服务的最佳选择。

资源

以下是您可能感兴趣的相关资源列表

  • Github 项目,博客的源代码就在其中。

  • 文章 涵盖了 Camel Quarkus 和 Camel K,同样基于 Quarkus。

  • Camel Quarkus 在 Apache Camel 上的主页。

  • AtlasMap 主页,加速您实现的视觉数据映射工具。

  • Apicurio 主页,用于创建 OpenAPI 合约的设计时工具。