使用 Quarkus 和 MicroProfile 实现微服务特性
Quarkus 的文章之前写过三篇了,讲过了 Quarkus 的小而快。
一直在酝酿写一篇 Quarkus 生态相关的,因为最近一直在忙 Meetup 的事情而搁浅。正好看到了这篇文章,就拿来翻译一下,补全云原生中的“微服务”这一块。
本文译自《Implementing Microservicilities with Quarkus and MicroProfile》 。
为什么要使用微服务特性?
在微服务架构中,一个应用程序是由几个相互连接的服务组成的,这些服务一起工作来实现所需的业务功能。
因此,典型的企业微服务架构如下所示:
刚开始,使用微服务架构实现应用程序看起来很容易。
但是,因为有了单体架构没有一些新的挑战,因此做起来并不容器
举几个例子,比如容错、服务发现、扩展性、日志记录和跟踪。
为了解决这些挑战,每个微服务都应实现我们在 Red Hat 所说的“微服务特性”。
[ ] 该术语是指除业务逻辑以外,服务还必须实现来解决的跨领域关注点清单,如下图所示:
可以用任何语言(Java、Go、JavaScript)或任何框架(Spring Boot、Quarkus)实现业务逻辑,但是围绕业务逻辑,应实现以下关注点:
API:可通过一组定义的 API 操作来访问该服务。例如,对于 RESTful Web API,HTTP 用作协议。此外,可以使用诸如 Swagger 之类的工具来记录 API 。服务发现(Discovery):服务需要发现其他服务。
调用服务(Invocation):发现服务后,需要使用一组参数对其进行调用,并选择性地返回响应。
弹性(Elasticity):微服务架构的重要特征之一是每个服务都是弹性的,这意味着可以根据系统的关键程度或当前的工作量等参数独立地进行缩放。(译者注:这里的弹性只是资源的弹性)
弹性(Resiliency):在微服务架构中,我们在开发时应牢记失败,尤其是在与其他服务进行通信时。在单体应用中,整个应用程序处于启动或关闭状态。但是,当此应用程序分解为微服务体系结构时,该应用程序由多个服务组成,并且所有这些服务都通过网络互连,这意味着该应用程序的某些部分可能正在运行,而其他部分可能会失败。遏制故障对避免通过其他服务传播错误很重要。弹性(或应用程序弹性)是应用程序/服务对问题做出反应并仍然提供最佳结果的能力。(译者注:这里的弹性与容错相关,对失败处理的弹性)
管道(Pipeline):服务应独立部署,而无需进行任何形式的编排。因此,每个服务应具有自己的部署管道。
身份验证(Authentication):关于微服务体系结构中的安全性的关键方面之一是如何对内部服务之间的调用进行身份验证/授权。Web 令牌(通常是令牌)是在内部服务中安全地表示声明的首选方式。
日志记录(Logging):在单体应用程序中,日志记录很简单,因为该应用程序的所有组件都在同一节点上运行。然后现在组件以服务的形式分布在多个节点上,因此,要拥有完整的日志记录视图,需要一个统一的日志记录系统/数据收集器。
监控(Monitoring):衡量系统的性能、了解应用程序的整体运行状况,以及在出现问题时发出警报是保持基于微服务的应用程序正确运行的关键方面。监控是控制应用程序的关键方面。
跟踪(Tracing):跟踪用于可视化程序的流程和数据进度。作为开发人员/运维人员,当我们需要检查用户在整个应用程序中的行程时,这特别有用。
Kubernetes 正在成为部署微服务的实际工具。这是一个用于自动化、编排、扩展和管理容器的开源系统。
使用 Kubernetes 时,十个微服务特性中只有三个被涵盖。
**服务发现 **是通过 Kubernetes 服务的概念实现的。它提供了一种使用稳定的虚拟 IP 和 DNS 名称将 Kubernetes Pod 分组(作为一个整体)的方法。发现服务只是使用 Kubernetes 的服务名作为 hostname 进行请求。
使用 Kubernetes 可以很容易地调用服务,因为平台本身提供了调用任何服务所需的网络。
从一开始,Kubernetes 就一直在考虑弹性(或可伸缩性),例如运行时kubectl scale deployment myservice --replicas=5 command
,myservice deployment 可伸缩至五个副本或实例。Kubernetes 平台负责寻找合适的节点,部署服务并始终保持所需数量的副本并正常运行。
但是其余的微服务特性又如何呢?Kubernetes 仅涵盖其中的三个,那么我们如何实现剩下的呢?
根据所使用的语言或框架,可以遵循的策略很多。但是在本文中,我们将了解如何使用 Quarkus 实现其中的一些策略。
什么是 Quarkus?
Quarkus 是针对 Java 虚拟机(JVM)和本机编译的全栈 Kubernetes 本地 Java 框架,专门针对容器优化 Java,使其成为无服务器(Serverless)、云和 Kubernetes 环境的高效平台。
Instead of reinventing the wheel, Quarkus uses well-known enterprise-grade frameworks backed by standards/specifications and makes them compilable to a binary using GraalVM.Quarkus 不用重新发明轮子,而是使用以标准/规范为后盾的知名企业级框架,并使用 GraalVM 将其编译为二进制文件。
什么是 MicroProfile?
Quarkus 与 MicroProfile 规范集成,从而将企业 Java 生态系统迁移到微服务体系结构中。
在下图中,我们看到了构成 MicroProfile 规范的所有 API。某些 API(例如 CDI、JSON-P 和 JAX-RS)基于 Jakarta EE(以前的 Java EE)规范。其余的由 Java 社区开发。
Let’s implement API, invocation, resilience, authentication, logging, monitoring, and tracing microservicilities using Quarkus.让我们使用 Quarkus 实现 API、调用、弹性、身份验证、日志记录、监视和跟踪微服务特性。
如何使用 Quarkus 实现微服务特性
入门
开始使用 Quarkus 的最快方法是通过在开始页面中选择所需的依赖。对于当前示例,选择如下依赖关系以满足微服务需求:
API:RESTEasy JAX-RS、RESTEasy JSON-B、OpenAPI 调用:REST Client JSON-B 弹性:Fault Tolerance 认证:JWT 记录:GELF 监控:Micrometer metrics 跟踪:OpenTracing
我们可以手动选择各自的依赖关系,或浏览以下链接 Quarkus 微服务特性生成器,所有这些都会被选中。然后按“生成应用程序”按钮以下载包含支架应用程序的 zip 文件。
服务
对于当前示例,仅使用两个服务生成了一个非常简单的应用程序。一个名为“评级服务 rating service”的服务返回给定书籍的评级,而另一个名为“书籍服务 book service”的服务则返回一本书的信息及其评级。服务之间的所有调用都必须经过身份验证。
在下图中,我们看到了整个系统的概述:
评级服务已经开发并作为 Linux 容器提供。通过运行以下命令,在端口 9090 上启动服务:
要验证服务,请向 http://localhost:9090/rate/1 发出请求
返回的状态码是 401 Unauthorized
因为没有在请求中携带令牌(JWT)提供授权信息。只有带有 group Echoer
有效令牌才能访问评级服务。
API
Quarkus 使用众所周知的 JAX-RS 规范来定义 RESTful Web API。在幕后,Quarkus 使用 RESTEasy 实现直接与 Vert.X 框架一起使用,而无需使用 Servlet 技术。
让我们为实现最常见操作的图书服务定义一个 API:
首先要注意的是,定义了四个不同的端点:
GET /book/{bookId}
使用 GET HTTP 方法返回带有其评级的图书信息。return 元素会自动解编为 JSON。POST /book
使用 POST HTTP 方法插入一本书作为正文内容。正文内容会自动从 JSON 编组到 Java 对象。DELETE /book/{bookId}
使用 DELETE HTTP 方法通过书的 ID 删除书。GET /book/search?description={description}
按书名搜索书籍。
注意的第二件事是返回类型,有时是 Java 对象,有时是 Java 实例 javax.ws.rs.core.Response
。使用 Java 对象时,会将其从 Java 对象序列化为 @Produces
注解中设置的媒体类型。在此特定服务中,输出为 JSON 文档。通过该 Response
对象,我们可以对返回给调用方的内容进行细粒度的控制。可以设置 HTTP 状态代码、标头或返回给调用方的内容。取决于使用场景,是偏爱一种方法而不是另一种方法。
调用
在定义了用于访问图书服务的 API 之后,是时候开发一段代码来调用评级服务以检索图书的评级了。
Quarkus 使用 MicroProfile Rest Client 规范来访问外部(HTTP)服务。它提供了一种类型安全的方法,以通过某些 JAX-RS 2.0 API 通过 HTTP 调用 RESTful 服务,以实现一致性和更易于重用。
要创建的第一个元素是一个使用 JAX-RS 批注表示远程服务的接口。
When the getRate() method is called, a remote HTTP call is invoked at /rate/{bookId} replacing the bookId with the value set in the method parameter. It is important to annotate the interface with the @RegisterRestClient annotation.Then the RatingService interface needs to be injected into BookResource to execute the remote calls.当 getRate()
方法被调用时,远程 HTTP 请求在调用 /rate/{bookId}
替换 bookId
用在该方法中的参数值集合。用 @RegisterRestClient
注解对接口进行注解很重要。
然后 RatingService
需要将接口注入 BookResource
以执行远程调用。
The @RestClient annotation injects a proxied instance of the interface, providing the implementation of the client.The last thing is to configure the service location (the hostname part). In Quarkus, the configuration properties are set in src/main/resources/application.properties file. To configure the location of the service, we need to use the fully qualified name of the Rest Client interface with URL as key, and the location as a value:该 @RestClient
注解注入界面的代理实例,提供客户端的实现。
最后一件事是配置服务位置(hostname 部分)。在 Quarkus 中,配置属性在 src/main/resources/application.properties
文件中设置。要配置服务的位置,我们需要使用 Rest Client 接口的标准名称,其中 URL 作为键,而 location 作为值:
在正确访问评估服务而没有 401 Unauthorized
问题之前,必须解决相互认证问题。
身份验证
基于令牌的身份验证机制允许系统基于安全令牌对身份进行身份验证、授权和验证。Quarkus 与 MicroProfile JWT RBAC 安全规范集成在一起,以使用 JWT 令牌保护服务。
要使用 MicroProfile JWT RBAC 安全性保护端点,我们只需要使用批注对方法进行 @RolesAllowed
注解。
然后,我们配置令牌的发行方和公钥的位置,以验证令牌在 application.properties
文件中的签名:
此扩展名自动验证:令牌有效;发行方是正确的;令牌尚未修改;签名有效;没有过期。
这两种图书服务和评级服务现在是由同一 JWT 发行方和密钥保护,因此服务之间的通信要求验证提供在令牌的有效承载用户 Authentication
头部。
评级服务启动和运行,让我们开始用下面的命令图书服务:
Finally, we can make a request to get book information providing a valid JSON Web Token as a bearer token.The generation of the token is out of the scope of this article, and a token has been already generated:最后,我们可以请求获取提供有效 JSON Web 令牌作为承载令牌的图书信息。
令牌的生成不在本文的讨论范围之内,并且已经生成了令牌:
响应又是 forbidden 错误:
你可能想知道为什么在提供有效令牌后仍然出现此错误。如果我们检查图书服务的控制台,就会发现抛出了以下异常:
发生此异常的原因是,我们已获得身份验证并有权访问图书服务,但承载令牌尚未传播到评级服务。
为了自动将 Authorization
标头从传入请求传播到其余客户端请求,需要进行两次修改。
第一个修改是修改 Rest Client 界面,并使用对其进行注解 org.eclipse.microprofile.rest.client.inject.RegisterClientHeaders
。
第二个修改是配置在请求之间传播哪些标头。这是在 application.properties
文件中设置的:
执行与之前相同的 curl 命令,我们将获得正确的输出:
弹性
在微服务架构中,具有容错能力很重要,这样可以避免故障从一个服务传播到该服务的所有直接和间接调用方。Quarkus 将 MicroProfile Fault Tolerance 规范与以下用于处理故障的注释集成在一起:
● @Timeout
:定义抛出异常之前执行的最长时间。● @Retry
:如果调用失败,请再次重试执行。● @Bulkhead
:限制并发执行,以使该区域中的故障不会使整个系统过载。● @CircuitBreaker
:执行反复失败时,将自动进行快速故障切换。● @Fallback
:执行失败时,提供备用解决方案/默认值。
让我们添加三次重试,其中重试之间的延迟计时器为一秒,以防访问评级服务时发生错误。
现在,停止评级服务并执行请求。引发以下异常:
显然,这里存在错误,但是请注意,由于执行了三次重试(延迟一秒),因此引发异常之前,经过了三秒钟。
在这种情况下,评级服务已关闭,因此无法进行恢复,但是在一个实际示例中,评级服务可能仅在短时间内就恢复了,或者部署了该服务的多个副本,因此可以简单地重试操作可能足以恢复并提供有效的响应。
但是,当引发异常时重试次数不够时,我们可以将错误传播给调用方,也可以为调用提供替代值。这种选择可以是对另一个系统的调用(即分布式缓存)或静态值。
对于此用例,当与评级服务的连接失败时,将返回评级值 0。
要实现回退逻辑,首先要做的是实现将 org.eclipse.microprofile.faulttolerance.FallbackHandler
返回类型设置为与回退策略方法提供的替代类型相同的接口。对于这种情况,将 Rate
返回默认对象。
最后要做的是用注解对 getRating()
方法进行 @org.eclipse.microprofile.faulttolerance.Fallback
注解,以配置无法恢复时要执行的回退类。
如果重复与以前相同的请求,则不会引发任何异常,但是有效值的输出将 rating 字段设置为 0。
规范提供的任何其他策略都可以使用相同的方法。例如,对于断路器模式:
如果在四个连续调用的滚动窗口中发生了三个(4 x 0.75)故障,则电路将断开 1000 ms,然后恢复到半断开状态。如果在半开时调用成功,则将其再次关闭。否则,它将保持打开状态
日志记录
在微服务架构中,建议将所有服务的日志收集在一个统一的日志中,以更有效地使用和理解。
一种解决方案是使用 Fluentd,它是 Kubernetes 中用于统一日志记录层的开源数据收集器。Quarkus 使用 Graylog 扩展日志格式(GELF)与 Fluentd 集成。
集成真的很简单。首先,与其他任何 Quarkus 应用程序一样使用日志逻辑:
接下来,启用 GELF 格式并设置 Fluentd 服务器位置:
最后,我们可以向记录的端点发出请求:
输出方面没有任何变化,但是日志行已传输到 Fluentd。如果使用 Kibana 可视化数据,我们将看到存储的日志行:
监控
Monitoring is another "microservicilitie" that needs to be implemented in our microservice architecture. Quarkus integrates with Micrometer for application monitoring. Micrometer provides a single entry point to the most popular monitoring systems, allowing you to instrument your JVM-based application code without vendor lock-in.
For this example, Prometheus format is used as monitoring output but Micrometer (and Quarkus) also supports other formats like Azure Monitor, Stackdriver, SignalFx, StatsD, and DataDog.
You can register the following Maven dependency to provide Prometheus output:监控是另一个需要在我们的微服务架构中实现的 “微服务特性”。Quarkus 与 Micrometer 集成在一起以进行应用程序监控。Micrometer 提供了最流行的监控系统的单个入口点,使你无需供应商锁定即可检测基于 JVM 的应用程序代码。
对于此示例,监控输出采用 Prometheus 格式,但 Micrometer(和 Quarkus)还支持其他格式,例如 Azure Monitor、Stackdriver、SignalFx、StatsD 和 DataDog。
你可以注册以下 Maven 依赖项以提供 Prometheus 输出:
默认情况下,Micrometer 扩展注册了一些与系统,JVM 或 HTTP 相关的度量。收集的指标的一个子集在 /q/metrics
端点处可用,如下所示:
但是,也可以使用 Micrometer API 来实现特定于应用程序的指标。让我们实现一个自定义指标,该指标用于衡量评价最高的图书。
使用 io.micrometer.core.instrument.MeterRegistry
该类可以完成指标(在这种情况下为量规)的注册。
请求一下,并验证量规是否正确更新。
我们还可以设置一个计时器来记录从评级服务获取评级信息所花费的时间。
请求一下,并验证收集评价所花费的时间。
Micrometer 使用 MeterFilter
实例来自定义 MeterRegistry
实例发出的度量。Micrometer 扩展将检测 MeterFilter
CDI bean,并在初始化 MeterRegistry
实例时使用它们。
例如,我们可以定义一个通用标签来设置运行应用程序的环境(产品、测试、预发布等)。
发送新请求并验证指标是否已标记。
请注意 env
包含值为 prod
的标签。
跟踪
Quarkus 应用程序利用 OpenTracing 规范为交互式 Web 应用程序提供分布式跟踪。
让我们配置 OpenTracing 以连接到 Jaeger 服务器,将 book-service 设置为服务名称以标识跟踪:
现在发一个请求:
访问 Jaeger UI 以验证是否跟踪了该调用:
总结
与开发整体应用程序相比,开发和实现微服务体系结构更具挑战性。我们认为,微服务可以驱动你根据应用程序基础结构正确地开发服务。
此处介绍的大多数微服务(API 和管道除外)是新的,或者在整体应用中实现方式有所不同。原因是现在应用程序被分解成几部分,所有部分都在网络中互连。
如果你 May 26, 2021 打算开发微服务并将其部署到 Kubernetes,那么 Quarkus 是一个很好的解决方案,因为它可以与 Kubernetes 顺利集成。实施大多数微服务很简单,只需要几行代码。
本文演示的源代码可以在 github 上找到。
关于作者
Alex Soto 是 Red Hat 开发人员经验总监。他对 Java 世界,软件自动化充满热情,并且他相信开源软件模型。Soto 是 Manning 的合著者 | 测试 Java 微服务和 O'Reilly Quarkus Cookbook 和几个开源项目的贡献者。自 2017 年以来一直是 Java 冠军,他还是 Salle URL University 的国际演讲者和老师。你可以在 Twitter (Alex Soto)上关注他,以随时了解 Kubernetes 和 Java 世界中正在发生的事情。
版权声明: 本文为 InfoQ 作者【张晓辉】的原创文章。
原文链接:【http://xie.infoq.cn/article/426e856c86eb59ea57394581c】。文章转载请联系作者。
评论