Eclipse-MicroProfile-企业级微服务实用指南-全-

Eclipse MicroProfile 企业级微服务实用指南(全)

原文:zh.annas-archive.org/md5/90EEB03D96FBA880C6AA42B87707D53C

译者:飞龙

协议:CC BY-NC-SA 4.0

序言

本书将帮助你了解 Eclipse MicroProfile,这是一个始于 2016 年的开源企业 Java 微服务规范,以及它的背景和历史、它对组织和企业的价值主张、它的社区治理、当前的 Eclipse MicroProfile 子项目(随着开源项目的演变,还会有更多的子项目加入)、它的实现以及它的互操作性。它还将为你提供 Eclipse MicroProfile 的未来发展方向的预览,一个在 Red Hat 的 Thorntail 实现中的 Eclipse MicroProfile 示例应用程序,Red Hat Runtimes 提供的一个运行时,以及关于在混合云和多云环境中运行 Eclipse MicroProfile 的指导和建议。本书将采用逐步的方法帮助你了解 Eclipse MicroProfile 项目和市场上的其实现。

本书适合哪些人

本书适合希望创建企业微服务的 Java 开发者。为了更好地利用这本书,你需要熟悉 Java EE 和微服务概念。

为了最大化地利用这本书

需要对微服务和企业 Java 有基本了解。其他安装和设置说明将在必要时提供。

下载示例代码文件

你可以从www.packt.com上你的账户下载本书的示例代码文件。如果你在其他地方购买了这本书,你可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给你。

你可以通过以下步骤下载代码文件:

  1. 登录或注册www.packt.com

  2. 选择支持标签。

  3. 点击代码下载。

  4. 在搜索框中输入本书的名称,并按照屏幕上的指示操作。

文件下载后,请确保使用最新版本进行解压缩或提取文件夹:

  • Windows 上的 WinRAR/7-Zip

  • Mac 上的 Zipeg/iZip/UnRarX

  • Linux 上的 7-Zip/PeaZip

本书的代码包也托管在 GitHub 上,地址为:github.com/PacktPublishing/Hands-On-Enterprise-Java-Microservices-with-Eclipse-MicroProfile ...

下载彩色图片

我们还将提供一个包含本书中使用的屏幕快照/图表的彩色图像的 PDF 文件。你可以在这里下载它:static.packt-cdn.com/downloads/9781838643102_ColorImages.pdf

使用的约定

本书中使用了多种文本约定。

CodeInText:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、假 URL、用户输入和 Twitter 处理。例如:"checks数组对象类型包括一个必需的namestatus字符串,以及一个可选的包含可选的keyvalue对的对象。"

代码块如下所示:

package org.eclipse.microprofile.health;@FunctionalInterfacepublic interface HealthCheck {  HealthCheckResponse call();}

任何命令行输入或输出如下所示:

Scotts-iMacPro:jwtprop starksm$ curl http://localhost:8080/jwt/secureHello; echoNot authorized

粗体:表示新的...

联系我们

来自读者的反馈总是受欢迎的。

一般反馈:如果你对本书的任何方面有问题,请在消息的主题中提及书名,并发送电子邮件至customercare@packtpub.com

错误报告:尽管我们已经采取了每一步来确保我们内容的准确性,但错误仍然会发生。如果你在这本书中发现了错误,我们将非常感激你能向我们报告。请访问www.packtpub.com/submit/errata,选择你的书,点击错误提交表单链接,并输入详情。

盗版:如果你在互联网上以任何形式发现我们作品的非法副本,如果你能提供位置地址或网站名称,我们将不胜感激。请通过copyright@packt.com联系我们,并附上材料的链接。

如果您有兴趣成为作者:如果您对某个主题有专业知识,并且有兴趣撰写或贡献一本书,请访问authors.packtpub.com

评论

请留下评论。一旦你阅读并使用了这本书,为什么不在这本书购买的网站上留下评论呢?潜在的读者可以看到和使用你的客观意见来做出购买决定,我们 Packt 可以了解你对我们的产品的看法,我们的作者可以看到你对他们书的反馈。谢谢!

关于 Packt 的更多信息,请访问packt.com

第一部分:数字经济中的 MicroProfile

在本部分,您将学习为什么在数字经济中微服务很重要,以及 MicroProfile 如何解决企业 Java 微服务的需求。此外,您还将了解目前组成 MicroProfile 的子项目、它对组织和开发者的价值主张以及其当前的过程和治理(即,事情是如何完成的)。

本部分包含以下章节:

  • 第一章,Eclipse MicroProfile 简介

  • 第二章,治理和贡献

第一章:Eclipse MicroProfile 介绍

Eclipse MicroProfile 是一套用 Java 编写的微服务规范。这是一个由社区驱动的项目,市场上有很多实现。该项目于 2016 年 6 月首次宣布,继续开发一套适用于现代应用程序开发技术、架构和环境的 Java 微服务常见应用程序编程接口APIs)。在本章中,您将了解 Eclipse MicroProfile 的起源和重要性。

本章将涵盖以下主题:

  • 企业级 Java 微服务

  • 推动数字经济的动力和多速度 IT 的需求

  • 介绍 Eclipse MicroProfile

  • MicroProfile...

企业级 Java 微服务

应用程序开发不再包括使用运行在你最喜欢的操作系统上的单一高级编程语言。如今,有许多语言、规范、框架、专有和开源软件及工具、底层部署基础设施和开发方法,程序员需要学习这些来开发现代应用程序。在 IT 组织中,开发已经变得多语言化了,也就是说,根据特定项目的需求,会使用多种编程语言。在这个云计算、容器、微服务、反应式编程、12 因子应用程序、无服务器、MINI 服务、多语言环境和等等的时代,开发者现在可以选择合适的工具来完成他们的任务,使他们更加有效和高效。

随着 Java EE 最近更名为 Jakarta EE,作为 Eclipse 基金会的一部分,MicroProfile 将在企业级 Java 的未来中发挥非常重要的作用,因为它与 Jakarta EE 的协同效应以及它可能对其产生影响的潜在方式。

云计算和移动设备的到来,以及开源的加速和物联网(IoT)的兴起,催生了数字经济。虽然这为新的市场打开了大门,但也对企业和他们的 IT 组织提出了新的要求,现在要求他们不仅要支持和维护传统的工作负载,还要更快地交付新的应用程序。

许多技术和语言、架构和框架在组织内变得流行,试图应对这些新的需求。其中一个是微服务,特别是企业级 Java 微服务,因为 Java 仍然是 IT 公司中最受欢迎的语言之一。但是什么是企业级 Java 微服务呢?

企业级 Java 微服务具有以下特点:

  • 它是用 Java 语言编写的。

  • 它可以使用任何 Java 框架。

  • 它可以使用任何 Java API。

  • 它必须是企业级的;也就是说,要可靠、可用、可扩展、安全、健壮且性能优良。

  • 它必须满足微服务架构的特征,如在 martinfowler.com/microservices/ 所列出的那样,具体如下:

    • 通过服务实现组件化

    • 围绕业务能力组织

    • 产品而非项目

    • 智能端点和哑管

    • 去中心化治理

    • 去中心化数据管理

    • 基础设施自动化

    • 设计容错

    • 进化式设计

推动数字经济发展的力量

术语数字经济数字化转型描述了改变企业需求的四个不同力量的汇聚:移动、云、物联网和开源:

在互联网出现之前,组织需要实体店铺或电话线来进行业务。互联网的出现和可访问性为组织创造了一个关键的类别形成时间的机会。企业开始主要使用互联网作为一个店面或展示,以吸引人们到他们的实体店铺。它也被用于广告目的。

不久之后,企业...

多速 IT

尽可能快地实施和交付应用程序并不是一个新的要求。实际上,自从第一台计算机的发明以来,提高效率一直计算机科学家的心头大事。高级编程语言、封装、可重用性、继承、事件驱动设计、SOA、微服务、机器学习和人工智能,都是旨在更快地完成事情的概念。随着每一波新技术的出现,变速箱为软件的开发和交付方式的演变增加了新的速度要求。数字经济又为变速箱增加了一个高速档位。

企业需要适应数字经济的新的要求。他们不仅要创建、运行和支持传统风格的应用程序,还要支持符合数字经济新要求的应用程序。他们必须支持瀑布和 DevOps 流程、混合云基础设施以及 SOA 和微服务架构。

这给 IT 组织带来了许多挑战,这些组织的流程、人员和技术都是为了实现、交付和维护传统风格的应用程序而设计的。许多组织已经开始了他们的数字化转型之旅,或者正准备开始,以应对数字经济带来的挑战。这一旅程包括改变应用程序开发、交付、集成和维护的技术、框架、语言和过程。

无论你称之为双模 IT(www.gartner.com/it-glossary/bimodal)还是业务技术战略(go.forrester.com/wp-content/uploads/Forrester-False-Promise-of-Bimodal-IT.pdf),事实是 IT 需要比以往任何时候都更快地满足业务需求,无论是现有还是新应用程序。这意味着 IT 还需要加快现有应用程序的维护和交付,同时专门为新的应用程序采用敏捷方法论。然而,这并不意味着不再使用不同的开发流程、发布周期和支持时间线来处理现有应用程序与新应用程序,这实际上是 IT 的多速度特性。

介绍 Eclipse MicroProfile

Java EE 已经成为一个极其成功的平台。Java 社区流程(Java Community Process,JCP)在其近 20 年的历史中,一直是超过 20 个兼容实现的监护人,促成了一个 400 亿美元产业。然而,Oracle 对 Java EE 的管理(无论是否无意)阻碍了创新,尽管其他标准已经发展,但全球 Java 社区和所有主要企业的 CIO 们都渴望在企业内部有一个开放的 Java 标准。

在早期阶段,J2EE 从 J2EE 1.2 迅速发展到 J2EE 1.4,因为该平台需要解决企业的迫切需求。从 2006 年 5 月 Java EE 5 开始,节奏开始变慢...

MicroProfile 价值主张

对于信任 Java EE 来运行其生产工作负载的客户,Eclipse MicroProfile 为客户提供了企业 Java 微服务的开源、中立规范。Eclipse MicroProfile 使客户能够通过提高敏捷性和可伸缩性、缩短上市时间、提高开发生产力、简化调试和维护以及持续集成和持续部署,更好地满足业务需求。

使用 Eclipse MicroProfile 的客户获得的好处与使用微服务的好处相同。总的来说,根据受人尊敬的软件开发者、作家和演讲者马丁·福勒的说法,微服务提供以下好处(martinfowler.com/articles/microservice-trade-offs.html):

  • 强模块边界:微服务加强了模块化结构,这对于大型团队尤为重要。

  • 独立部署:简单的服务更易于部署,由于它们是自主的,因此在出错时更不可能导致系统故障。

  • 技术多样性:通过微服务,你可以混合多种语言、开发框架和数据存储技术。

除了微服务的通用优势外,Eclipse MicroProfile 还特别提供了以下优势:

  • 社区协作的优势:Eclipse MicroProfile 是一个由社区运行的开源项目。没有单一的供应商控制或决定规格说明书的演变和成熟。

  • 实施选择的自由:许多供应商已将 Eclipse MicroProfile 作为其软件堆栈的一部分实现,客户可以选择最适合其环境的任何实现。

  • 更快的演变:由于 Eclipse MicroProfile 是一个创新项目,新的和改进的功能经常在时间盒发布的周期内交付。这使得开发人员和客户能够掌握这些功能,并尽早而不是稍后在他们的项目中利用更新。

  • 基于数十年的经验:不仅规格说明书的主题专家们带来了丰富的经验、专业知识和技能,Eclipse MicroProfile 还利用了在 Java EE API 中经过市场检验和生产验证的功能,它在此基础上构建,为开发者提供了成熟度。

  • 熟悉企业 Java:Eclipse MicroProfile 建立在熟悉的企业 Java 构造之上,使得企业 Java 开发者易于采用。

  • 无需重新培训:您的现有企业 Java 开发人员会发现 Eclipse MicroProfile 是他们专业知识的自然进步。学习曲线很小甚至没有。他们将能够利用他们的技能。

  • 互操作性:不同的 MicroProfile 实现是互操作的,每个实现都为用户提供自由选择一个或结合许多 MicroProfile 实现于应用程序中的能力。

  • 多种使用 API 的方式:Eclipse MicroProfile API 提供了易于使用的接口,如基于 CDI、程序化、命令行和基于文件(配置 based)的接口。

  • 一套完整的工件:每个 API 都包括一个测试兼容性套件TCK)、Javadoc、可下载的 PDF 文档、API Maven 工件坐标、Git 标签和下载(规格说明书和源代码)。

  • 每个 API 特有的许多其他优势。这些在本书的每个 Eclipse MicroProfile 子项目中讨论。

总结

在本章中,我们讨论了软件开发的新趋势,包括使用新方法(如微服务、容器、移动和物联网(IoT))的多元语言部署,这些新方法运行在本地和云上;以及在混合或多云环境中。这些趋势要求 Enterprise Java 在微服务世界中进行演变,这就是 MicroProfile 所解决的问题。推动数字经济的四大力量,即云、移动、IoT 和开源,导致了组织需要多速度 IT 部门,这是维护和演变现有应用程序以及利用新技术趋势开发新应用程序的必要条件...

问题

  1. 企业级 Java 微服务是什么?

  2. 推动数字经济的四大力量是什么?

  3. 为什么 IT 组织必须以不同的速度开发和维护应用程序?多速度 IT 是什么?

  4. 为什么 Java 和 Java EE 对组织仍然重要?

  5. 是什么关键原因导致了 MicroProfile 的产生?

  6. 哪些 API/规范是 MicroProfile 伞/平台发布的一部分?

  7. 哪个版本的 MicroProfile 引入了第一次革命性的变化?

  8. 为什么 MicroProfile 对组织有价值?

第二章:治理和贡献

Eclipse MicroProfile 由社区成员治理。换句话说,它不是由单一的供应商来治理。此外,它还收到了来自开发者以及来自各种组织、公司和个体贡献者的贡献。该项目以其轻量级的过程和治理而著称,通过这些过程和治理,实现了创新、快速和敏捷。本章中的主题将帮助您了解 MicroProfile 项目的治理,您还将发现如何也能为 MicroProfile 项目做出贡献。

本章将涵盖以下主题:

  • 如何治理 Eclipse MicroProfile 项目

  • 社区如何协作并为其持续创新做出贡献……

当前的 Eclipse MicroProfile 治理

Eclipse MicroProfile 在其运营和决策过程中是透明的,这些过程旨在非常轻量级。治理重点是创建、创新和以协作方式发展规范。

Eclipse MicroProfile 首先是一个 Eclipse 项目,因此,它遵循 Eclipse 的流程。这包括提交者批准、项目发布、知识产权保护、许可证审查流程等。然而,Eclipse 基金会足够灵活,可以让像 MicroProfile 这样的项目提供一些额外的轻量级流程,以便多个规范可以并行推进,并有方法进行跨规范沟通和对齐规范。

其中一个轻量级过程是 Eclipse MicroProfile 的两周一次的 Hangout 会议/通话(其会议 URL 在 eclipse.zoom.us/j/949859967,其录音可以在 Eclipse MicroProfile YouTube 频道上找到,该频道地址为 www.youtube.com/channel/UC_Uqc8MYFDoCItFIGheMD_w),该会议对社区中的任何人开放,作为一个论坛,讨论与会者提出的话题并做出决定,包括子项目状态、发布内容、发布日期和子项目创建批准等。需要注意的是,尽管 MicroProfile 看起来像一个标准化组织,但它并不是。MicroProfile 是由社区创建的,它以社区确定的速度在不同子项目中进行创新。MicroProfile 定义的规范鼓励多种实现,这与标准化组织的做法类似。然而,MicroProfile 实际上是一个快速发展的开源项目,其源代码就是规范。

社区沟通、讨论和辩论的主要方式是 Eclipse MicroProfile Google 组(groups.google.com/forum/#!forum/microprofile). 你可以使用你喜欢的网络浏览器在 Google 组中阅读、发布、回答或开始关于任何与 MicroProfile 相关的主题的论坛消息。你还可以使用该组的电子邮件来发起新的论坛消息。任何人都可以发起新的论坛线程来讨论话题,比如要添加到 MicroProfile 的潜在新功能。在社区在论坛上就新想法进行长时间讨论,并且/或者在 MicroProfile Hangout 通话中讨论后,确定值得进一步辩论,社区决定为这个新想法创建一个工作组,并指定一名负责人或一组负责人,他们通常是处理主题的专家,担任其协调员。

需要指出的一点重要方面是,工作组(或子项目)的负责人或负责人并不单独塑造或决定一个规范的演变或包括哪些功能以及不包括哪些功能。他们没有否决权或在关于他们规范的决策中没有最终决定权。通过分享想法、专业知识、过去经验、现有技术的分析以及最佳实践,工作组将提出他们最好的提案。此外,所有未解决的问题需要由社区讨论,并在需要时在两周一次的 Hangout 会议/通话中提出进一步辩论。通过讨论、协作和来自社区的反馈,分析了许多观点,使最佳选项或选项浮出水面。工作组将建立一个重复的每周或每两周一次的会议,该会议记录在 MicroProfile Google 日历中(calendar.google.com/calendar/embed?src=gbnbc373ga40n0tvbl88nkc3r4%40group.calendar.google.com). 这包含所有 MicroProfile Hangout 通话、MicroProfile 子项目通话和 MicroProfile 发布日期等信息。

虽然任何人都可以参加这些会议,但通常有一小部分人作为主题专家参与这些通话。在几次会议之后,工作组决定是否将新功能带到 MicroProfile Hangout 通话中讨论其成为 MicroProfile 子项目的提案。

在 MicroProfile Hangout 电话会议中,一个子项目提案可能会被拒绝或接受。应该说,当子项目提案带到 MicroProfile Hangout 电话会议时,是否应该向前推进的讨论大多数已经完成,所以会议上的决定对子项目工作组来说真的不应该感到惊讶。子项目的拒绝并不意味着它不满足一个特定的发展需求,而是对它的目标与推进 MicroProfile 规范的目标不匹配的肯定,该规范的目标是优化企业 Java 微服务架构。

例如,如果一个子项目提案解决了一个与微服务无关的需求,那么这个子项目提案很可能不会作为 MicroProfile 子项目向前推进。子项目的接受意味着它有效地解决了一个需求,丰富了规范,使其朝着优化企业 Java 微服务架构的目标前进。正是在这个时候,子项目变成了一个官方的 MicroProfile API。一旦子项目变成了一个 MicroProfile API,那么就需要决定它是作为一个独立子项目存在于 umbrella 之外,还是作为包含在 umbrella 中的子项目发布。这个过程的高级流程图如下:

在撰写本书时,这些都是 Eclipse MicroProfile API/子项目(列出项目负责人):

MicroProfile API/子项目名称 子项目负责人
MicroProfile 项目负责人 约翰·克莱根和凯文·舒特
配置 姜艾米莉和马克·斯特鲁布伯格
容错 姜艾米莉
健康检查 安托万·萨博-迪兰
JWT 传播 斯科特·斯塔克
指标 海因科·鲁普
OpenAPI 阿图尔·德·马加良埃斯
OpenTracing 帕沃·洛法伊
Rest 客户端 约翰·D·阿门特和安迪·麦克莱恩特

Eclipse MicroProfile 遵循一个时间盒式的快速增量发布计划,该计划是公开的,列在 Eclipse 基金会 MicroProfile 项目页面(projects.eclipse.org/projects/technology.microprofile)上。例如,从 1.x 到 2.x 的主要 Eclipse MicroProfile 发布,包括对 MicroProfile API 的重大更新,可能会引入破坏性变化。次要发布,即点发布,包括小的 API 更新或新的 API,以确定的发布日期为准。目前,MicroProfile 社区的发布窗口每年在二月、六月和十一月,适用于次要和/或主要发布。

开放贡献的沙箱方法

为潜在的 MicroProfile 子项目创建的工作组也可能被分配一个沙盒,这是 MicroProfile 社区提供尝试新想法的另一个资源。沙盒仓库是一个位于 github.com/eclipse/microprofile-sandbox 的 GitHub 仓库,用于孵化想法和代码示例,这些最终将变成新规范的独立仓库。任何人都可以打开拉取请求,并使用沙盒进行新想法的实验,以及分享代码和文档,这些可以作为社区 Google 组、MicroProfile 闲聊电话或工作组会议讨论的一部分。保持您的拉取请求开放...

伞状发布与伞状之外的项目

Eclipse MicroProfile 是由一系列具有特定焦点的规范组成。例如,Eclipse MicroProfile Config 规范涵盖了所有与为微服务配置参数相关的内容。一个规范的版本可以作为 Eclipse MicroProfile 的伞状发布的一部分包含在内,或者在伞状之外发布。作为一个具体的例子,Eclipse MicroProfile 2.2 的最新伞状发布,于 2019 年 2 月 12 日发布,包括了以下规范:

  • Eclipse MicroProfile Open Tracing 1.3

  • Eclipse MicroProfile Open API 1.1

  • Eclipse MicroProfile Rest Client 1.2

  • Eclipse MicroProfile Fault Tolerance 2.0

  • Eclipse MicroProfile Config 1.3

  • Eclipse MicroProfile Metrics 1.1

  • Eclipse MicroProfile JWT Propagation 1.1

  • Eclipse MicroProfile Health Check 1.0

  • CDI 2.0

  • JSON-P 1.1

  • JAX-RS 2.1

  • JSON-B 1.0

然而,Eclipse MicroProfile 还有其他一些在非伞状发布中发布的规范。例如,我们在第九章“响应式编程与未来发展”中介绍的 Eclipse MicroProfile Reactive Streams Operators 1.0,就是一个最近在非伞状发布中发布的规范。那么,为什么 MicroProfile 允许在伞状之外的规范呢?原因在于,首先在伞状之外发布,可以为社区和最终用户提供使用和测试新技术的机会,因此在被纳入伞状之前,在真实应用中验证这些技术。

MicroProfile Starter

MicroProfile Starter 是一个示例源代码生成器,其目标是帮助开发者快速开始使用和利用由社区驱动的开源企业 Java 微服务规范 Eclipse MicroProfile,通过在 Maven 项目中生成可工作的示例代码。

自 2016 年中旬项目创建以来,就有了 MicroProfile Starter 的想法,并在 2016 年 Devoxx BE(2016 年 11 月 7 日那周)上公开讨论。在 MicroProfile Starter 项目发布的前两周里,世界各地的开发者通过这个项目创建了超过 1200 个项目,这是对其在全球范围内采用的一个良好和积极的迹象。

快速游览 MicroProfile Starter

让我们快速了解一下 MicroProfile Starter:

  1. 当你访问MicroProfile Starter "Beta"页面,start.microprofile.io/,你会看到以下登录页面:

对于 Maven 相关的参数,你可以接受默认值(maven.apache.org/guides/mini/guide-naming-conventions.html),groupId 和 artifactId,或者按照喜好更改。groupId 参数能唯一标识你的项目,artifactId 是 JAR 文件名,不包含 MicroProfile 版本号。在这个游览中,接受所有默认值。

  1. 接下来,从下拉列表中选择 MicroProfile Version:

在这个游览中,选择 MicroProfile 版本 MP 2.1。注意,根据你选择的 MicroProfile 版本,示例规格部分列出的规格数量会有所变化。这个数字取决于每个 MicroProfile 伞状发布中包含多少 API。要了解每个版本中包含哪些 API,请参考 MicroProfile 社区演讲(docs.google.com/presentation/d/1BYfVqnBIffh-QDIrPyromwc9YSwIbsawGUECSsrSQB0/edit#slide=id.g4ef35057a0_6_205)。

  1. 然后,从下拉列表中选择 MicroProfile Server:

在这个游览中,选择 Thorntail V2,这是红帽用来实现 Eclipse MicroProfile 规范的开源项目。

  1. 保留所有示例规格复选框的选择(也就是说,不要取消选中任何复选框):

这将生成包含 MicroProfile 版本 2.1 中所有 API 的示例工作代码。

  1. 使用 MicroProfile Starter 生成示例源代码过程的最后一步是点击 DOWNLOAD 按钮,这将创建一个 ZIP 归档文件。确保将demo.zip文件保存到你的本地驱动器上。然后在你的本地驱动器上解压demo.zip。内容应如下所示:

请注意生成的内容中有一个readme.md文件。这个文件包含了如何编译和运行生成代码的说明,这包括一个练习 Eclipse MicroProfile 不同功能的示例网络应用程序。

  1. 更改目录到你解压演示项目的位置。在我的情况下,我把它放在了我的Downloads目录中:
$ cd Downloads/demo
  1. 通过输入以下命令编译生成的示例代码:
$ mvn clean package
  1. 运行微服务:
$ java -jar target/demo-thorntail.jar
  1. 几秒钟后,你将看到以下消息:
$ INFO  [org.wildfly.swarm] (main) WFSWARM99999: Thorntail is Ready

这表明微服务正在运行。

  1. 打开你喜欢的网页浏览器,指向http://localhost:8080/index.html

这将打开示例网络应用程序,如下所示:

  1. 要查看 MicroProfile Config 的功能,点击注入的配置值。将打开一个窗口标签,显示以下内容:

  1. 同样,如果你点击通过查找查看配置值,将显示另一个窗口标签如下:

我们之前看到的参数值的注入值查找值定义在./demo/src/main/resources/META-INF/microprofile-config.properties文件中,如下所示:

$ cat ./src/main/resources/META-INF/microprofile-config.properties
injected.value=Injected value
value=lookup value
  1. 假设你需要为value参数在开发和系统测试之间使用不同的值。你可以通过在启动微服务时传递命令行参数来实现这一点(首先确保按终端窗口上的Ctrl + C退出运行中的应用程序):
$ java -jar target/demo-thorntail.jar -Dvalue=hola
  1. 现在,当你点击通过查找查看配置值时,将显示另一个窗口标签:

请注意,执行此逻辑的源代码位于生成的./src/main/java/com/example/demo/config/ConfigTestController.java文件中。

  1. 要查看 MicroProfile Fault Tolerance 的功能,点击超时后的回退。将打开一个窗口标签,显示以下内容:

有关 MicroProfile Config API 的更多信息,请参阅其文档(github.com/eclipse/microprofile-config/releases/download/1.3/microprofile-config-spec-1.3.pdf)。

示例代码演示了@Fallback注解与@Timeout的组合使用。以下是示例代码:

@Fallback(fallbackMethod = "fallback") // fallback handler
   @Timeout(500)
   @GET
   public String checkTimeout() {
     try {
       Thread.sleep(700L);
     } catch (InterruptedException e) {
       //
     }
     return "Never from normal processing";
   }
   public String fallback() {
   return "Fallback answer due to timeout";
   }

@Timeout注解指定如果方法执行时间超过 500 毫秒,应抛出超时异常。此注解可以与@Fallback一起使用,在这种情况下,当发生超时异常时调用回退处理程序。在前面的示例代码中,超时异常总是发生,因为方法正在执行——即,休眠 700 毫秒,这超过了 500 毫秒。

请注意,执行此逻辑的源代码位于生成的./src/main/java/com/example/demo/resilient/ResilienceController.java文件中。

有关 MicroProfile 容错 API 的更多信息,请参阅其文档(github.com/eclipse/microprofile-opentracing/releases/download/1.2/microprofile-opentracing-spec-1.2.pdf)。

微 Profile 社区欢迎您提供反馈以及与微 Profile Starter 项目的协作或贡献。要提供反馈,您需要点击 MicroProfile Starter "Beta"页面右上角的“提供反馈”按钮(start.microprofile.io/)并创建一个问题。

微 Profile Starter 项目会将请求的项目和修复按照里程碑进行分组和优先排序,目标是持续发布。MicroProfile Starter 工作组定期召开会议,如果您想用您的开发技能帮助该项目,请发送电子邮件至microprofile@googlegroups.com或加入其 Gitter 频道讨论(gitter.im/eclipse/microprofile-starter)。项目的信息,包括源代码的位置,可以在wiki.eclipse.org/MicroProfile/StarterPage找到。

总结

在本章中,我们学习了 Eclipse 微 Profile 项目的轻量级治理流程,其快速的创新方法,以及如何使用沙盒促进协作和鼓励代码开发和文档。我们还了解了其子项目,这些子项目的领导者,以及它们可以作为独立版本发布,或者作为 Eclipse 微 Profile 伞式发布的一部分发布。

此外,您还了解了 MicroProfile Starter,这是一个 Maven 项目生成工具,提供代码示例,以便开发者可以快速启动他们的 MicroProfile 应用程序。最后,我们简要介绍了如何使用 Eclipse MicroProfile Config 构造轻松修改应用程序的属性...

问题

  1. 微 Profile 社区的主要沟通方式是什么?

  2. 双周一次的 MicroProfile Hangout 电话会议的目标是什么?

  3. 子项目(微 Profile 规范)负责人/负责人的角色是什么?

  4. 一个新的微 Profile 规范提案遵循的过程是什么?

  5. 微 Profile 项目遵循的发布计划是什么?

  6. 微 Profile 沙盒的目标是什么?

  7. 在伞式/平台 MicroProfile 发布下发布项目和在外部发布的项目有什么区别?

  8. 微 Profile Starter 是什么,它提供了哪些好处?

第三章:第二节:MicroProfile 的当前功能

本节将介绍项目的功能及其子项目的功能,并提供代码示例。

本节包含以下章节:

  • 第三章,MicroProfile 配置和容错

  • 第四章,MicroProfile 健康检查和 JWT 传播

  • 第五章,MicroProfile 指标和 OpenTracing

  • 第六章,MicroProfile OpenAPI 和类型安全 REST 客户端

第四章:微服务配置和容错

在本章中,我们将首先介绍微服务配置,因为它是其他微服务功能配置的基础,除了应用程序级配置。微服务配置规范提供了一种通用方法,用于从各种来源(属性文件、系统属性、环境变量、数据库等)检索配置。

我们将涵盖的主题包括以下内容:

  • 从您的应用程序读取配置

  • 为您的应用程序提供额外的配置源

  • 提供将普通配置转换为特定于应用程序对象的转换

理解 Eclipse 微服务配置

每个应用程序都需要一些外部配置,以使其行为适应其正在运行的运行时平台。这可以从应用程序必须连接到的 HTTP 端点,或者某些内部结构的大小。

这些配置参数也可以来自不同的来源:

  • 从操作系统或云原生环境中的容器(通过使用环境变量)

  • 从 Java 虚拟机(通过系统属性)

  • 从一些外部配置文件(如 Java 属性文件)

  • 从其他地方(如 LDAP 服务器、数据库、键值存储等)

一方面,这些配置参数来自许多不同的来源。在...

从微服务配置 API 读取配置

微服务配置规范定义了两个对象来读取配置参数的值:

  • 使用Config对象以编程方式访问配置值

  • @ConfigProperty注解用于使用上下文和依赖注入CDI)注入配置值

让我们详细讨论它们。

配置对象

org.eclipse.microprofile.config.Config接口是检索 Java 应用程序中配置的入口点。

获取Config实例有两种方法:

  1. 第一种(且首选)方法是使用 CDI 将其注入代码中:
@Inject
private Config config;
  1. 第二种方法是调用静态方法org.eclipse.microprofile.config.ConfigProvider#getConfig(),以获取Config实例:
Config config = ConfigProvider.getConfig();

Config接口提供两种检索属性的方法:

  • getValue(String propertyName, Class propertyType):如果配置中不存在该属性,此方法将抛出运行时异常。仅对于必需的配置才应使用此方法 ...

@ConfigProperty注解

@ConfigProperty注解可用于使用 CDI 将配置值注入 Java 字段或方法参数,如所示:

@Inject
@ConfigProperty(name="my.url")
private URL myURL;

@ConfigProperty注解可以有一个defaultValue,如果在底层的Config中找不到配置属性,则用于配置字段:

@Inject
@ConfigProperty(name="my.url", defaultValue="http://localhost/")
private URL myURL;

如果未设置defaultValue且未找到任何属性,应用程序将抛出DeploymentException,因为无法正确配置。

如果一个配置属性可能不存在,可以使用Optional,如下面的代码块所示:

@Inject
@ConfigProperty(name="my.url")
private Optional<URL> someUrl; // will be set to Optional.empty if the
                               // property `my.url` cannot be found

在读取配置之后,我们需要提供源配置源,这部分将在下一节中介绍。

提供配置源

配置源由ConfigSource接口表示。除非你想提供在你应用程序中使用的 MicroProfile 实现不可用的配置源,否则你不需要实现这个接口。

如果在多个配置源中找到了一个属性,Config将返回ConfigSource接口中序号最高的值。

排序ConfigSource很重要,因为用户可以提供自定义的ConfigSource接口,这除了由 MicroProfile Config 实现提供的默认接口。

默认的 ConfigSources

默认情况下,一个 MicroProfile Config 实现必须提供三个配置源:

  • 来自 Java 虚拟机系统的属性(序号为400

  • 环境变量(序号为300

  • 存储在META-INF/microprofile-config.properties中的属性(序号为100

配置源的ordinal值决定了配置源的优先级。特别是,如果一个属性既在系统属性中定义,又在环境变量中定义,将采用系统属性的值(其序号高于环境变量)。

属性名没有限制。然而,一些操作系统可能会对环境变量的名称施加一些限制(例如,大多数 Unix 壳牌不允许".")。如果您有一个可能从环境变量中配置的属性,您必须相应地命名您的属性。

例如,属性名my_url可以由环境变量使用,而my.url则不行。

MicroProfile Config 1.3 新特性 MicroProfile Config 1.3 引入了一个从配置属性名到环境变量的映射规则。这个规则为每个属性名搜索三个环境变量的变体:

  • 精确匹配

  • 将任何非字母数字字符替换为_

  • 将任何非字母数字字符替换为_并使用大写字母

这意味着,在 Java 应用程序中,我们可以有一个名为app.auth.url的属性,并使用APP_AUTH_URL环境变量来配置它。

接下来让我们看看另一种配置源。

自定义 ConfigSources 实现

在您的应用程序中,可以提供额外的配置源,这些源将由 MicroProfile Config 实现自动添加。

你需要定义一个org.eclipse.microprofile.config.spi.ConfigSource的实现,并为它添加一个 Java ServiceLoader配置,并将该文件放在你的应用程序归档中作为META-INF/services/ org.eclipse.microprofile.config.spi.ConfigSource。供您参考,以下是一个环境ConfigSource实现定义的示例:

package io.packt.sample.config;import java.io.Serializable;import java.util.Collections;import java.util.Map;import org.eclipse.microprofile.config.spi.ConfigSource;public class EnvConfigSource ...

使用转换器进行高级配置

MicroProfile Config 将从其ConfigSource读取 Java String对象。然而,它提供了将这些String对象转换为应用程序中更具体类型的设施。

例如,我们之前描述的myUrl字段是一个URL对象。相应的属性my.urlString对象的形式读取,然后在被注入之前转换为URL对象。

如果应用程序使用Config对象,MicroProfile Config 实现也将把String对象转换为getValuegetOptionalValue方法的第二个参数传递的类型。这个转换可以使用不同的转换器类型:内置、自动和自定义。我们现在将详细讨论它们。

内置转换器

MicroProfile Config 实现为基本类型(booleanintlongbytefloatdouble)及其对应的 Java 类型(例如Integer)提供了内置转换器。

它还提供了使用","作为项目分隔符在属性值中支持数组的功能。如果","必须是项目的一部分,它必须用反斜杠"\"转义:

private String[] pets = config.getValue("myPets", String[].class)

如果myPets属性的值是dog,cat,dog\\,cat,存储在pets中的数组的元素将是{"dog", "cat", "dog,cat"}

自动转换器

MicroProfile Config 还定义了自动转换器。如果一个转换器对给定的 Java 类型未知,它将尝试使用三种不同的方法之一将String对象转换为它:

  • Java 类型有一个带有String参数的公共构造函数。

  • 它有一个public static valueOf(String)方法。

  • 它有一个public static parse(String)方法。

这就是my.url属性如何从String转换为URL,因为java.net.URL类型有一个public URL(String)构造函数。

自定义转换器

如果你的应用程序定义了 Java 类型,这些类型不提供自动转换器涵盖的这三个案例,MicroProfile Config 仍然可以使用自定义转换器提供转换,这些转换器扩展了以下定义的org.eclipse.microprofile.config.spi.Converter接口:

public interface Converter<T> {
    /**
     * Configure the string value to a specified type
     * @param value the string representation of a property value.
     * @return the converted value or null
     *
     * @throws IllegalArgumentException if the value cannot be converted to        the specified type.
     */
    T convert(String value);

你必须编写一个org.eclipse.microprofile.config.spi.Converter的实现,然后将其名称添加到/META-INF/services/org.eclipse.microprofile.config.spi.Converter ...

理解 Eclipse MicroProfile 容错

容错提供了一组工具,通过使代码更具弹性来防止代码失败。其中大多数工具受到了良好的开发实践(如重试或回退)或知名开发模式(如断路器或隔舱)的启发。

容错基于 CDI ,更确切地说,是基于 CDI 拦截器实现。它还依赖于 MicroProfile Config 规范,以允许为容错策略提供外部配置。

规范的主要思想是将业务逻辑与容错样板代码解耦。为了实现这一点,规范定义了拦截器绑定注解,以在方法执行或类上(在这种情况下,所有类方法具有相同的策略)应用容错策略。

容错规范中包含的政策如下:

  • 超时:这是使用@Timeout注解 applied。它在当前操作中添加了超时。

  • 重试:这是使用@Retry注解 applied。它添加了重试行为,并允许对当前操作进行配置。

  • 回退:这是使用@Fallback注解 applied。它定义了在当前操作失败时应执行的代码。

  • ** bulkhead**:这是使用@Bulkhead注解 applied。它将当前操作中的故障隔离以保留其他操作的执行。

  • 断路器:这是使用@CircuitBreaker注解 applied。它提供了一个自动快速失败的执行,以防止系统过载。

  • 异步:这是使用@Asynchronous注解 applied。它将当前操作设置为异步(也就是说,代码将异步调用)。

应用这些策略之一或多个就像在需要这些策略的方法(或类)上添加所需的注解一样简单。所以,使用容错是非常简单的。但这种简单性并没有阻止灵活性,感谢所有可用配置参数每个策略。

目前,以下供应商为 Fault Tolerance 规范提供实现:

  • Red Hat in Thorntail and Quarkus

  • IBM in Open Liberty

  • Payara in Payara Server

  • Apache Safeguard for Hammock and TomEE

  • KumuluzEE for KumuluzEE framework

所有这些实现都支持容错,因此支持下一节中描述的相同一组功能。

MicroProfile Fault Tolerance in action

正如我们刚刚讨论的,Fault Tolerance 规范提供了一组注解,您需要将这些注解应用于类或方法以强制执行容错策略。话虽如此,您必须记住这些注解是拦截器绑定,因此仅适用于 CDI bean。所以,在将容错注解应用于它们或其方法之前,请务必小心地将您的类定义为 CDI beans。

在以下各节中,您将找到每个容错注解的用法示例。

The @Asynchronous policy

使操作异步就像以下这样简单:

@Asynchronous
public Future<Connection> service() throws InterruptedException {
  Connection conn = new Connection() {
    {
      Thread.sleep(1000);
    }

    @Override
    public String getData() {
      return "service DATA";
    }
 };
 return CompletableFuture.completedFuture(conn);
}

唯一的限制是@Asynchronous方法必须返回FutureCompletionStage;否则,实现应该抛出异常。

@Retry策略

如果操作失败,你可以应用重试策略,以便再次调用操作。@Retry注解可以像这样应用于类或方法级别:

@Retry(maxRetries = 5, maxDuration= 1000, retryOn = {IOException.class})public void operationToRetry() {  ...}

在前一个示例中,操作应该只在发生IOException时最多重试五次。如果所有重试的总持续时间超过 1,000 毫秒,则操作将被取消。

@Fallback策略

@Fallback注解只能应用于方法;注释类将产生意外结果:

@Retry(maxRetries = 2)
@Fallback(StringFallbackHandler.class)
public String shouldFallback() {
  ...
}

在达到重试次数后调用回退方法。在前一个示例中,如果出现错误,方法将重试两次,然后使用回退调用另一段代码——在这个例子中,下面的StringFallbackHandler类:

import javax.enterprise.context.ApplicationScoped;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.faulttolerance.ExecutionContext;
import org.eclipse.microprofile.faulttolerance.FallbackHandler;

@ApplicationScoped
public class StringFallbackHandler implements FallbackHandler<String> {
    @ConfigProperty(name="app1.requestFallbackReply", defaultValue = "Unconfigured Default Reply")
    private String replyString;

    @Override
    public String handle(ExecutionContext ec) {
        return replyString;
    }
}

可以通过实现FallbackHandler接口的类或当前 bean 中的方法定义回退代码(见前一个代码)。在StringFallbackHandler代码中,使用了名为app1.requestFallbackReply的 MicroProfile Config 属性来外部化应用程序的回退字符串值。

@Timeout策略

@Timeout注解可以应用于类或方法,以确保操作不会永远持续:

@Timeout(200)public void operationCouldTimeout() {  ...}

在前一个示例中,如果操作持续时间超过 200 毫秒,则将停止操作。

@CircuitBreaker策略

@CircuitBreaker注解可以应用于类或方法。电路断路器模式由马丁·福勒引入,旨在通过使其在功能失调时快速失败来保护操作的执行:

@CircuitBreaker(requestVolumeThreshold = 4, failureRatio=0.75, delay = 1000)
public void operationCouldBeShortCircuited(){
  ...
}

在前一个示例中,方法应用了CircuitBreaker策略。如果在四个连续调用滚动窗口中发生三次(4 x 0.75)失败,则电路将被打开。电路将在 1,000 毫秒后保持打开状态,然后回到半开状态。在一个成功的调用之后,电路将再次关闭。

@Bulkhead策略

@Bulkhead注解也可以应用于类或方法以强制执行 bulkhead 策略。这种模式通过限制给定方法的同时调用次数来隔离当前操作中的故障,以保留其他操作的执行。

@Bulkhead(4)public void bulkheadedOperation() {  ...}

在前一个代码中,这个方法只支持四个同时调用。如果超过四个同时请求进入bulkheadedOperation方法,系统将保留第五个及以后的请求,直到四个活动调用之一完成。bulkhead 注解还可以与@Asynchronous结合使用,以限制异步中的线程数...

微配置中的容错性

正如我们在前几节所看到的,Fault Tolerance 策略是通过使用注解来应用的。对于大多数用例来说,这已经足够了,但对于其他一些情况,这种方法可能不令人满意,因为配置是在源代码级别完成的。

这就是为什么 MicroProfile Fault Tolerance 注解的参数可以通过 MicroProfile Config 进行覆盖的原因。

注解参数可以通过使用以下命名约定来通过配置属性覆盖:<classname>/<methodname>/<annotation>/<parameter>

要覆盖MyService类中doSomething方法的@RetrymaxDuration,请像这样设置配置属性:

org.example.microservice.MyService/doSomething/Retry/maxDuration=3000

如果需要为特定类配置与特定注解相同的参数值,请使用<classname>/<annotation>/<parameter>配置属性进行配置。

例如,使用以下配置属性来覆盖MyService类上@Retry的所有maxRetries为 100:

org.example.microservice.MyService/Retry/maxRetries=100

有时,需要为整个微服务(即在部署中所有注解的出现)配置相同的参数值。

在这种情况下,<annotation>/<parameter>配置属性将覆盖指定注解的相应参数值。例如,要覆盖所有@RetrymaxRetries为 30,请指定以下配置属性:

Retry/maxRetries=30

这使我们结束了关于 MicroProfile 中的 Fault Tolerance 的讨论。

总结

在本章中,我们学习了如何使用 MicroProfile Config 来配置 MicroProfile 应用程序以及 MicroProfile Fault Tolerance 使其更具弹性。

在 MicroProfile Config 中,配置来源可以有很多;一些值来自属性文件,其他来自系统属性或环境变量,但它们都是从 Java 应用程序一致访问的。这些值可能会根据部署环境(例如,测试和生产)而有所不同,但在应用程序代码中这是透明的。

MicroProfile Fault Tolerance 通过在代码中应用特定策略来防止应用程序失败。它带有默认行为,但可以通过 MicroProfile 进行配置...

问题

  1. MicroProfile Config 支持哪些默认的配置属性来源?

  2. 如果您需要将另一个配置属性源集成在一起,您可以做什么?

  3. 只支持字符串类型的属性吗?

  4. 将配置属性注入代码是否迫使您为该属性提供值?

  5. 假设您有复杂的属性类型。是否有方法将它们集成到 MicroProfile Config 中?

  6. 当一个 Fault Tolerance 注解应用于一个类时会发生什么?

  7. 真或假:至少有 10 种不同的 Fault Tolerance 策略?

  8. @Retry策略是否需要在所有失败上进行重试?

  9. 我们是否必须使用应用程序代码中使用的 Fault Tolerance 注解设置?

进一步阅读

关于 MicroProfile Config 特性的更多详细信息,可以在github.com/eclipse/microprofile-config/releases的 MicroProfile Config 规范中找到。关于 MicroProfile Fault Tolerance 特性的更多详细信息,可以在github.com/eclipse/microprofile-fault-tolerance/releases的 MicroProfile Config 规范中找到。

第五章:MicroProfile 健康检查与 JWT 传播

在本章中,我们将介绍健康检查项目及其关注点,它们的构造方式,以及在应用程序中如何使用它们。本章中的代码片段仅供参考。如果您需要一个工作的代码版本,请参考第八章,一个工作的 Eclipse MicroProfile 代码示例

我们将涵盖以下主题:

  • 健康检查是什么

  • MicroProfile 健康检查如何暴露健康检查端点和查询该端点的格式

  • 如何为您的应用程序编写 MicroProfile 健康检查

  • MicroProfile JWT 传播中令牌的所需格式

  • 如何利用 MicroProfile JWT 传播进行安全决策

技术要求

要构建和运行本章中的示例,您需要 Maven 3.5+和 Java 8 JDK。本章的代码可以在github.com/PacktPublishing/Hands-On-Enterprise-Java-Microservices-with-Eclipse-MicroProfile/tree/master/Chapter04-healthcheckgithub.com/PacktPublishing/Hands-On-Enterprise-Java-Microservices-with-Eclipse-MicroProfile/tree/master/Chapter04-jwtpropagation找到,分别对应于 MicroProfile 健康检查和 MicroProfile 传播 JWT 部分。

理解健康检查以及 MicroProfile 如何处理它们

在云原生架构中,健康检查用于确定计算节点是否存活并准备好执行工作。就绪状态描述了容器启动或滚动更新(即,重新部署)时的状态。在此期间,云平台需要确保没有网络流量路由到该实例,直到它准备好执行工作。

生存性,另一方面,描述运行容器的状态;也就是说,当容器启动或滚动更新(即重新部署)时,它处于就绪状态。在此期间,云平台需要确保没有网络流量路由到该实例,直到它准备好执行工作。

健康检查是与云平台调度程序和应用程序编排框架之间的基本合同。检查程序由应用程序开发者提供,平台使用这些程序来持续确保应用程序或服务的可用性。

微服务健康检查 1.0(MP-HC)支持一个单一的健康检查端点,可以用于活动或就绪检查。微服务健康检查 2.0 计划添加对多个端点的支持,以允许应用程序定义活动和就绪探测器。

微服务健康检查规范详细介绍了两个元素:一个协议以及响应线缆格式部分和一个用于定义响应内容的 Java API。

微服务健康检查(MP-HC)特性的架构被建模为一个由零个或多个逻辑链接在一起的健康检查过程组成的应用程序,这些过程通过AND来推导出整体健康检查状态。一个过程代表了一个应用程序定义的检查一个必要条件的操作,它有一个名字、状态,以及可选的关于检查的数据。

健康检查协议和线缆格式

微服务健康检查规范定义了支持对逻辑/health REST 端点的 HTTP GET 请求的要求,该端点可能返回以下任一代码来表示端点的状态:

  • 200:它正在运行并且是健康的。

  • 500:由于未知错误而不健康。

  • 503:它已经关闭,无法对请求做出响应。

请注意,许多云环境只是将请求返回代码视为成功或失败,所以500503代码之间的区别可能无法区分。

/health请求的负载必须是一个与下面给出的架构匹配的 JSON 对象(有关 JSON 架构语法的更多信息,请参见jsonschema.net/#/)。

下面是...

健康检查 Java API

大部分工作由实现微服务健康检查规范的应用程序框架完成。你的任务是通过使用微服务健康检查(MP-HC)API 定义的健康检查过程来决定活动或就绪是如何确定的。

为了实现这一点,你需要通过使用带有Health注解的 beans 实现一个或多个HealthCheck接口实例来执行健康检查过程。

HealthCheck接口如下:

package org.eclipse.microprofile.health;

@FunctionalInterface
public interface HealthCheck {
  HealthCheckResponse call();
}

Health注解的代码如下:

package org.eclipse.microprofile.health;

import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Health {
}

下面的例子展示了一个表示假设磁盘空间检查状态的HealthCheck实现。注意检查将当前的空闲空间作为响应数据的一部分。HealthCheckResponse类支持一个构建器接口来填充响应对象。

下面是一个假设的磁盘空间HealthCheck过程实现:

import javax.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.health.Health;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;

@Health
@ApplicationScoped
public class CheckDiskspace implements HealthCheck {
  @Override
  public HealthCheckResponse call() {
      return HealthCheckResponse.named("diskspace")
              .withData("free", "780mb")
              .up()
              .build();
  }
}

在这个例子中,我们创建了一个名为diskspace的健康响应,其状态为up,并带有名为free的自定义数据,其字符串值为780mb

下面的例子展示了一个表示某些服务端点的健康检查示例。

下面展示了一个假设的服务HealthCheck过程实现:

package io.packt.hc.rest;
//ServiceCheck example

import javax.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.health.Health;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;

@Health
@ApplicationScoped
public class ServiceCheck implements HealthCheck {
 public HealthCheckResponse call() {
 return HealthCheckResponse.named("service-check")
 .withData("port", 12345)
 .withData("isSecure", true)
 .withData("hostname", "service.jboss.com")
 .up()
 .build();
 }
}

在这个例子中,我们创建了一个名为service-check的健康响应,其状态为up,并包括了以下附加数据:

  • 一个整数值为12345port

  • 一个值为true的布尔isSecure

  • 一个值为service.jboss.com的字符串hostname

由 CDI 管理的健康检查由应用程序运行时自动发现和注册。运行时自动暴露一个 HTTP 端点,/health,供云平台用来探测您的应用程序状态。您可以通过构建Chapter04-healthcheck应用程序并运行它来测试这一点。您将看到以下输出:

Scotts-iMacPro:hc starksm$ mvn package
[INFO] Scanning for projects…
...
Resolving 144 out of 420 artifacts

[INFO] Repackaging .war: /Users/starksm/Dev/JBoss/Microprofile/PacktBook/Chapter04-metricsandhc/hc/target/health-check.war

[INFO] Repackaged .war: /Users/starksm/Dev/JBoss/Microprofile/PacktBook/Chapter04-metricsandhc/hc/target/health-check.war

[INFO] -----------------------------------------------------------------------

[INFO] BUILD SUCCESS

[INFO] -----------------------------------------------------------------------

[INFO] Total time:  7.660 s

[INFO] Finished at: 2019-04-16T21:55:14-07:00

[INFO] -----------------------------------------------------------------------

Scotts-iMacPro:hc starksm$ java -jar target/health-check-thorntail.jar

2019-04-16 21:57:03,305 INFO  [org.wildfly.swarm] (main) THORN0013: Installed fraction: MicroProfile Fault Tolerance - STABLE          io.thorntail:microprofile-fault-tolerance:2.4.0.Final

…

2019-04-16 21:57:07,449 INFO  [org.jboss.as.server] (main) WFLYSRV0010: Deployed "health-check.war" (runtime-name : "health-check.war")

2019-04-16 21:57:07,453 INFO  [org.wildfly.swarm] (main) THORN99999: Thorntail is Ready

一旦服务器启动,通过查询健康端点来测试健康检查:

Scotts-iMacPro:Microprofile starksm$ curl -s http://localhost:8080/health | jq
{
 "outcome": "UP",
 "checks": [
   {
     "name": "service-check",
     "state": "UP",
     "data": {
       "hostname": "service.jboss.com",
       "port": 12345,
       "isSecure": true
     }
   },
   {
     "name": "diskspace",
     "state": "UP",
     "data": {
       "free": "780mb"
     }
   }
 ]
}

这显示了整体健康状况为UP。整体状态是应用程序中找到的所有健康检查程序的逻辑OR。在这个例子中,它是我们所看到的两个健康检查程序diskspaceservice-checkAND

与云平台的集成

大多数云平台都支持基于 TCP 和 HTTP 的检查。为了将健康检查与您选择的云平台集成,您需要配置云部署,使其指向托管应用程序的节点上的 HTTP 入口点,/health

云平台将调用 HTTP 入口点的GET查询;所有注册的检查都将执行,个别检查的总和决定了整体结果。

通常,响应载荷被云平台忽略,它只查看 HTTP 状态码来确定应用程序的存活或就绪状态。成功的成果,UP,将被映射到200,而DOWN将被映射到503

人类操作者

JSON 响应载荷的主要用例是提供一种让操作者调查应用程序状态的方式。为了支持这一点,健康检查允许将附加数据附加到健康检查响应中,正如我们在CheckDiskspaceServiceCheck示例中所看到的那样。考虑以下片段:

[...]
return HealthCheckResponse
           .named("memory-check")
           .withData("free-heap", "64mb")
           .up()
           .build();
[...]

在这里,提供了关于free-heap的附加信息,并将成为响应载荷的一部分,正如这个响应片段所示。显示memory-check程序内容的 JSON 响应片段如下:

{
...
   "checks": [
       {
           "name": "memory-check",
           "state": "UP",
           "data": {
               "free-heap": "64mb"
           }
       }
   ],
   "outcome": "UP"
}

在这里,我们看到memory-check程序以其UP状态和字符串类型的附加free-heap数据项,值为64mb

Eclipse 资源/GitHub 中 MP-Health 的坐标

MP-Health 项目源代码可以在github.com/eclipse/microprofile-health找到。

健康检查响应消息的变化

MicroProfile Health Check 3.0 对健康检查 JSON 响应的消息格式进行了更改。具体来说,字段的成果和状态已经被字段状态所取代。

此外,在健康检查 3.0 版本中,@Health限定符已被弃用,而@Liveness@Readiness限定符已被引入。对于这两个限定符,还引入了/health/live/health/ready端点,分别调用所有存活性和就绪性程序。最后,为了向后兼容,/health端点现在会调用所有具有@Health@Liveness@Readiness限定符的程序。

是时候讨论 JWT 传播了。

在 MicroProfile 中使用 JSON Web Token 传播

一个JSON Web TokenJWT)是许多不同的基于 web 的安全协议中用于携带安全信息的一种常见格式。然而,JWT 的确切内容以及与已签名 JWT 一起使用的安全算法缺乏标准化。微服务 JWTMP-JWT)传播项目规范审视了基于OpenID ConnectOIDC)的 JWT(openid.net/connect/)规范,并在这些规范的基础上定义了一组需求,以促进基于 MicroProfile 的微服务中 JWT 的互操作性,同时还提供了从 JWT 中访问信息的 API。

有关 OIDC 和 JWT 如何工作的描述,包括应用程序/微服务如何拦截承载令牌,请参阅openid.net/connect/上的基本客户端实现指南

在本节中,您将了解以下内容:

  • 为了互操作性,所需的 OIDC 和 JWT 规范中的声明和签名算法

  • 使用 JWT 进行基于角色的访问控制RBAC)的微服务端点

  • 如何使用 MP-JWT API 来访问 JWT 及其声明值

互操作性的建议

MP-JWT 作为令牌格式的最大效用取决于身份提供商和服务提供商之间的协议。这意味着负责发行令牌的身份提供商应该能够以服务提供商可以理解的方式发行 MP-JWT 格式的令牌,以便服务提供商可以检查令牌并获取有关主题的信息。MP-JWT 的主要目标如下:

  • 它应该可以用作认证令牌。

  • 它应该可以用作包含通过组声明间接授予的应用级角色的授权令牌。

  • 它支持 IANA JWT 分配(www.iana.org/assignments/jwt/jwt.xhtml)中描述的额外标准声明,以及非标准...

必需的 MP-JWT 声明

需要提供支持的 MP-JWT 声明集合包括以下内容:

  • typ:此头部参数标识令牌类型,必须是JWT

  • alg:此头部算法用于签署 JWT,必须是RS256

  • kid:这个头部参数提供了一个提示,关于用哪个公钥签署了 JWT。

  • iss:这是令牌的发行者和签名者。

  • sub:这标识了 JWT 的主题。

  • exp:这标识了 JWT 在或之后过期的时刻,此时 JWT 不得被处理。

  • iat:这标识了 JWT 的发行时间,可以用来确定 JWT 的年龄。

  • jti:这为 JWT 提供了一个唯一标识符。

  • upn:这是 MP-JWT 自定义声明,是指定用户主体名称的首选方式。

  • groups:这是 MP-JWT 自定义声明,用于分配给 JWT 主体的组或角色名称列表。

NumericDate用于expiat和其他日期相关声明是一个表示从1970-01-01T00:00:00Z UTC 直到指定 UTC 日期/时间的 JSON 数值值,忽略闰秒。此外,有关标准声明的更多详细信息可以在 MP-JWT 规范(github.com/eclipse/microprofile-jwt-auth/releases/tag/1.1.1)和 JSON Web Token RFC(tools.ietf.org/html/rfc7519)中找到。

一个基本的 MP-JWT 的 JSON 示例将是与 MP-JWT 兼容的 JWT 的示例头和载荷,如下所示:

{
    "typ": "JWT",
    "alg": "RS256",
    "kid": "abc-1234567890"
}
{
    "iss": "https://server.example.com",
    "jti": "a-123",
    "exp": 1311281970,
    "iat": 1311280970,
    "sub": "24400320",
    "upn": "jdoe@server.example.com",
    "groups": ["red-group", "green-group", "admin-group", "admin"],
}
{
*** base64 signature not shown ***
}

此示例显示了具有typ=JWTalg=RS256kid=abc-1234567890的头部。正文包括issjtiexpiatsubupngroups声明。

MP-JWT API 的高级描述

MP-JWT 项目在org.eclipse.microprofile.jwt包命名空间下引入了以下 API 接口和类:

  • JsonWebToken:这是java.security.Principal接口的一个扩展,通过 get 风格的访问器提供所需声明的集合,同时提供对 JWT 中任何声明的通用访问。

  • Claims:这是一个封装了所有标准 JWT 相关声明以及描述和返回自JsonWebToken#getClaim(String)方法的声明所需 Java 类型的枚举实用类。

  • Claim:这是一个用于指示ClaimValue注入点的限定注解。

  • ClaimValue<T>:这是java.security.Principal接口的一个扩展...

使用 MP-JWT 的示例代码

MP-JWT API 的基本用法是注入JsonWebToken、其ClaimValue或两者。在本节中,我们将展示典型用法的代码片段。本书本节代码可在github.com/PacktPublishing/Hands-On-Enterprise-Java-Microservices-with-Eclipse-MicroProfile/tree/master/Chapter04-jwtpropagation找到。

注入 JsonWebToken 信息

以下代码示例说明了如何访问传入的 MP-JWT 令牌作为JsonWebToken、原始 JWT 令牌字符串、upn声明以及与 JAX-RSSecurityContext的集成:

package io.pckt.jwt.rest;import javax.annotation.security.DenyAll;import javax.annotation.security.PermitAll;import javax.annotation.security.RolesAllowed;import javax.inject.Inject;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.Produces;import javax.ws.rs.core.Context;import javax.ws.rs.core.MediaType;import javax.ws.rs.core.SecurityContext;import org.eclipse.microprofile.jwt.Claim;import org.eclipse.microprofile.jwt.Claims;import org.eclipse.microprofile.jwt.JsonWebToken;@Path("/jwt")@DenyAll //1public class ...

向 JWT 断言值注入

本节中的代码片段说明了 JWT 断言值的注入。我们可以使用几种不同的格式作为注入值。标准断言支持在Claim#getType字段和JsonValue子类型中定义的对象子类型。自定义断言类型只支持JsonValue子类型的注入。

以下代码示例说明了标准groupsiss断言的注入,以及customStringcustomIntegercustomDoublecustomObject自定义断言的注入:

package io.pckt.jwt.rest;

import java.util.Set;
import javax.annotation.security.DenyAll;
import javax.annotation.security.RolesAllowed;
import javax.inject.Inject;
import javax.json.JsonArray;
import javax.json.JsonNumber;
import javax.json.JsonObject;
import javax.json.JsonString;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.eclipse.microprofile.jwt.Claim;
import org.eclipse.microprofile.jwt.Claims;

@Path("/jwt")
@DenyAll
public class InjectionExampleEndpoint {
    @Inject
    @Claim(standard = Claims.groups)
    Set<String> rolesSet; // 1
    @Inject
    @Claim(standard = Claims.iss)
    String issuer; // 2

    @Inject
    @Claim(standard = Claims.groups)
    JsonArray rolesAsJson; // 3
    @Inject
    @Claim(standard = Claims.iss)
    JsonString issuerAsJson; // 4
    // Custom claims as JsonValue types
    @Inject
    @Claim("customString")
    JsonString customString; // 5
    @Inject
    @Claim("customInteger")
    JsonNumber customInteger; // 6
    @Inject
    @Claim("customDouble")
    JsonNumber customDouble; // 7
    @Inject
    @Claim("customObject")
    JsonObject customObject; // 8

    @GET
    @Path("/printClaims")
    @RolesAllowed("Tester")
    public String printClaims() {
        return String.format("rolesSet=%s\n");
    }
}

八个注释注入如下:

  1. 将标准groups断言作为其默认Set<String>类型注入

  2. 将标准iss断言作为其默认字符串类型注入

  3. 将标准groups断言作为其默认JsonArray类型注入

  4. 将标准iss断言作为其默认JsonString类型注入

  5. JsonString类型的customString断言中注入非标准自定义字符串

  6. JsonNumber类型的非标准customInteger断言中注入

  7. JsonNumber类型的非标准customDouble断言中注入

  8. JsonString类型的customObject断言中注入非标准自定义对象

配置 JWT 的认证

为了接受 JWT 作为应进行认证并因此受信任的身份,我们需要配置 MP-JWT 功能以验证谁签署了 JWT 以及谁发布了 JWT。这是通过 MP-Config 属性完成的:

  • mp.jwt.verify.publickey:这提供了 MP-JWT 签名者的公钥材料,通常以 PKCS8 PEM 格式嵌入。

  • mp.jwt.verify.issuer:这指定了 JWT 中iss断言的预期值。

本书的一个microprofile-configuration.properties文件示例如下:

# MP-JWT Configmp.jwt.verify.publickey=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEqFyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwRTYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5eUF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYnsIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9xnQIDAQAB ...

运行示例

我们查看的示例可以部署到 Thorntail,并通过针对端点的命令行查询来验证预期行为。由于需要对带有安全约束的端点进行认证,因此我们需要一种生成将被 Thorntail 服务器接受的有效 JWT 的方法。

本章代码提供了一个io.packt.jwt.test.GenerateToken工具,该工具将由配置在 Thorntail 服务器上的密钥签发的 JWT。JWT 中包含的断言由本章项目的src/test/resources/JwtClaims.json文档定义。您使用mvn exec:java命令运行该工具,如下所示:

Scotts-iMacPro:jwtprop starksm$ mvn exec:java -Dexec.mainClass=io.packt.jwt.test.GenerateToken -Dexec.classpathScope=test
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< io.microprofile.jwt:jwt-propagation >-----------------
[INFO] Building JWT Propagation 1.0-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ jwt-propagation ---
Setting exp: 1555684338 / Fri Apr 19 07:32:18 PDT 2019
 Added claim: sub, value: 24400320
 Added claim: customIntegerArray, value: [0,1,2,3]
 Added claim: customDoubleArray, value: [0.1,1.1,2.2,3.3,4.4]
 Added claim: iss, value: http://io.packt.jwt
 Added claim: groups, value: 
    ["Echoer","Tester","User","group1","group2"]
 Added claim: preferred_username, value: jdoe
 Added claim: customStringArray, value: ["value0","value1","value2"]
 Added claim: aud, value: [s6BhdRkqt3]
 Added claim: upn, value: jdoe@example.com
 Added claim: customInteger, value: 123456789
 Added claim: auth_time, value: 1555683738
 Added claim: customObject, value: {"my-service":{"roles":["role-in-my-
    service"],"groups":["group1","group2"]},"service-B":{"roles":["role-in-
    B"]},"service-C":{"groups":["groupC","web-tier"]},"scale":0.625}
 Added claim: exp, value: Fri Apr 19 07:32:18 PDT 2019
 Added claim: customDouble, value: 3.141592653589793
 Added claim: iat, value: Fri Apr 19 07:22:18 PDT 2019
 Added claim: jti, value: a-123
 Added claim: customString, value: customStringValue
eyJraWQiOiJcL3ByaXZhdGUta2V5LnBlbSIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiIyNDQwMDMyMCIsImN1c3RvbUludGVnZXJBcnJheSI6WzAsMSwyLDNdLCJjdXN0b21Eb3VibGVBcnJheSI6WzAuMSwxLjEsMi4yLDMuMyw0LjRdLCJpc3MiOiJodHRwOlwvXC9pby5wYWNrdC5qd3QiLCJncm91cHMiOlsiRWNob2VyIiwiVGVzdGVyIiwiVXNlciIsImdyb3VwMSIsImdyb3VwMiJdLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqZG9lIiwiY3VzdG9tU3RyaW5nQXJyYXkiOlsidmFsdWUwIiwidmFsdWUxIiwidmFsdWUyIl0sImF1ZCI6InM2QmhkUmtxdDMiLCJ1cG4iOiJqZG9lQGV4YW1wbGUuY29tIiwiY3VzdG9tSW50ZWdlciI6MTIzNDU2Nzg5LCJhdXRoX3RpbWUiOjE1NTU2ODM3MzgsImN1c3RvbU9iamVjdCI6eyJteS1zZXJ2aWNlIjp7InJvbGVzIjpbInJvbGUtaW4tbXktc2VydmljZSJdLCJncm91cHMiOlsiZ3JvdXAxIiwiZ3JvdXAyIl19LCJzZXJ2aWNlLUIiOnsicm9sZXMiOlsicm9sZS1pbi1CIl19LCJzZXJ2aWNlLUMiOnsiZ3JvdXBzIjpbImdyb3VwQyIsIndlYi10aWVyIl19LCJzY2FsZSI6MC42MjV9LCJleHAiOjE1NTU2ODQzMzgsImN1c3RvbURvdWJsZSI6My4xNDE1OTI2NTM1ODk3OTMsImlhdCI6MTU1NTY4MzczOCwianRpIjoiYS0xMjMiLCJjdXN0b21TdHJpbmciOiJjdXN0b21TdHJpbmdWYWx1ZSJ9.bF7CnutcQnA2gTlCRNOp4QMmWTWhwP86cSiPCSxWr8N36FG79YC9Lx0Ugr-Ioo2Zw35z0Z0xEwjAQdKkkKYU9_1GsXiJgfYqzWS-XxEtwhiinD0hUK2qiBcEHcY-ETx-bsJud8_mSlrzEvrJEeX58Xy1Om1FxnjuiqmfBJxNaotxECScDcDMMH-DeA1Z-nrJ3-0sdKNW6QxOxoR_RNrpci1F9y4pg-eYOd8zE4tN_QbT3KkdMm91xPhv7QkKm71pnHxC0H4SmQJVEAX6bxdD5lAzlNYrEMAJyyEgKuJeHTxH8qzM-0FQHzrG3Yhnxax2x3Xd-6JtEbU-_E_3HRxvvw
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.339 s
[INFO] Finished at: 2019-04-19T07:22:19-07:00
[INFO] ------------------------------------------------------------------------

该工具输出了添加的断言,然后打印出 Base64 编码的 JWT。您将使用这个 JWT 作为curl命令行中访问服务器端点的Authorization: Bearer …头部的值。

为了启动带有示例端点的 Thorntail 服务器,请进入Chapter04-jwtpropagation项目目录,然后运行mvn以构建可执行的 JAR:

Scotts-iMacPro:jwtprop starksm$ mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< io.microprofile.jwt:jwt-propagation >-----------------
[INFO] Building JWT Propagation 1.0-SNAPSHOT
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  8.457 s
[INFO] Finished at: 2019-04-19T08:25:09-07:00
[INFO] ------------------------------------------------------------------------

生成的可执行 JAR 位于target/jwt-propagation-thorntail.jar。你使用本章的示例部署和java -jar …命令启动 Thorntail 服务器:

Scotts-iMacPro:jwtprop starksm$ java -jar target/jwt-propagation-thorntail.jar
2019-04-19 08:27:33,425 INFO  [org.wildfly.swarm] (main) THORN0013: Installed fraction: MicroProfile Fault Tolerance - STABLE          io.thorntail:microprofile-fault-tolerance:2.4.0.Final
2019-04-19 08:27:33,493 INFO  [org.wildfly.swarm] (main) THORN0013: Installed fraction:          Bean Validation - STABLE io.thorntail:bean-validation:2.4.0.Final
2019-04-19 08:27:33,493 INFO  [org.wildfly.swarm] (main) THORN0013: Installed fraction:      MicroProfile Config - STABLE io.thorntail:microprofile-config:2.4.0.Final
2019-04-19 08:27:33,493 INFO  [org.wildfly.swarm] (main) THORN0013: Installed fraction:             Transactions - STABLE io.thorntail:transactions:2.4.0.Final
2019-04-19 08:27:33,494 INFO  [org.wildfly.swarm] (main) THORN0013: Installed fraction:        CDI Configuration - STABLE io.thorntail:cdi-config:2.4.0.Final
2019-04-19 08:27:33,494 INFO  [org.wildfly.swarm] (main) THORN0013: Installed fraction: MicroProfile JWT RBAC Auth - STABLE          io.thorntail:microprofile-jwt:2.4.0.Final
…
2019-04-19 08:27:37,708 INFO  [org.jboss.as.server] (main) WFLYSRV0010: Deployed "jwt-propagation.war" (runtime-name : "jwt-propagation.war")
2019-04-19 08:27:37,713 INFO  [org.wildfly.swarm] (main) THORN99999: Thorntail is Ready

在此阶段,我们可以查询服务器端点。有一个端点是我们定义的,不需要任何认证。这是io.pckt.jwt.rest.SecureEndpoint类的jwt/openHello端点。运行以下命令来验证你的 Thorntail 服务器是否如预期运行:

Scotts-iMacPro:jwtprop starksm$ curl http://localhost:8080/jwt/openHello; echo
Hello[open] user=anonymous, upn=no-upn

接下来,尝试受保护的端点。它应该会因为未提供任何授权信息而失败,返回 401 未授权错误:

Scotts-iMacPro:jwtprop starksm$ curl http://localhost:8080/jwt/secureHello; echo
Not authorized

现在,我们需要生成一个新的 JWT,并将其与 curl 命令一起在Authorization头中传递,让我们试一试。我们将使用 mvn 命令生成的 JWT 在 JWT 环境变量中保存,以简化 curl 命令行:

Scotts-iMacPro:jwtprop starksm$ mvn exec:java -Dexec.mainClass=io.packt.jwt.test.GenerateToken -Dexec.classpathScope=test
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< io.microprofile.jwt:jwt-propagation >-----------------
[INFO] Building JWT Propagation 1.0-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ jwt-propagation ---
Setting exp: 1555688712 / Fri Apr 19 08:45:12 PDT 2019
 Added claim: sub, value: 24400320
 Added claim: customIntegerArray, value: [0,1,2,3]
 Added claim: customDoubleArray, value: [0.1,1.1,2.2,3.3,4.4]
 Added claim: iss, value: http://io.packt.jwt
 Added claim: groups, value: 
    ["Echoer","Tester","User","group1","group2"]
 Added claim: preferred_username, value: jdoe
 Added claim: customStringArray, value: ["value0","value1","value2"]
 Added claim: aud, value: [s6BhdRkqt3]
 Added claim: upn, value: jdoe@example.com
 Added claim: customInteger, value: 123456789
 Added claim: auth_time, value: 1555688112
 Added claim: customObject, value: {"my-service":{"roles":["role-in-my-
    service"],"groups":["group1","group2"]},"service-B":{"roles":["role-in-
    B"]},"service-C":{"groups":["groupC","web-tier"]},"scale":0.625}
 Added claim: exp, value: Fri Apr 19 08:45:12 PDT 2019
 Added claim: customDouble, value: 3.141592653589793
 Added claim: iat, value: Fri Apr 19 08:35:12 PDT 2019
 Added claim: jti, value: a-123
 Added claim: customString, value: customStringValue
eyJraWQiOiJ...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.352 s
[INFO] Finished at: 2019-04-19T08:35:12-07:00
[INFO] ------------------------------------------------------------------------
Scotts-iMacPro:jwtprop starksm$ JWT="eyJraWQiOi..."
Scotts-iMacPro:jwtprop starksm$ curl -H "Authorization: Bearer $JWT" http://localhost:8080/jwt/secureHello; echo
Hello[secure] user=jdoe@example.com, upn=jdoe@example.com, scheme=MP-JWT, isUserRole=true

在前面的代码片段中,对于 Windows 用户,请为 Windows 安装一个与 bash 兼容的壳程序;否则,由于echo命令错误,你将遇到错误。

这次,查询成功,我们看到用户名upn声明值、方案和isUserInRole("User")检查都如预期一样。

现在,尝试访问/jwt/printClaims端点,该端点说明了标准和非标准声明作为不同类型的注入:

Scotts-iMacPro:jwtprop starksm$ curl -H "Authorization: Bearer $JWT" http://localhost:8080/jwt/printClaims
+++ Standard claims as primitive types
rolesSet=[Echoer, Tester, User, group2, group1]
issuer=http://io.packt.jwt
+++ Standard claims as JSON types
rolesAsJson=["Echoer","Tester","User","group2","group1"]
issuerAsJson="http://io.packt.jwt"
+++ Custom claim JSON types
customString="customStringValue"
customInteger=123456789
customDouble=3.141592653589793
customObject={"my-service":{"roles":["role-in-my-service"],"groups":["group1","group2"]},"service-B":{"roles":["role-in-B"]},"service-C":{"groups":["groupC","web-tier"]},"scale":0.625}

请注意,如果你在使用了一段时间后开始遇到未授权错误,问题是因为 JWT 已经过期。你需要生成一个新的令牌,或者生成一个有效期更长的令牌。你可以通过向GenerateToken工具传入以秒为单位的过期时间来做到这一点。例如,为了生成一个可以使用一小时的令牌,执行以下操作:

Scotts-iMacPro:jwtprop starksm$ mvn exec:java -Dexec.mainClass=io.packt.jwt.test.GenerateToken -Dexec.classpathScope=test -Dexec.args="3600"
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< io.microprofile.jwt:jwt-propagation >-----------------
[INFO] Building JWT Propagation 1.0-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ jwt-propagation ---
Setting exp: 1555692188 / Fri Apr 19 09:43:08 PDT 2019
 Added claim: sub, value: 24400320
 Added claim: customIntegerArray, value: [0,1,2,3]
 Added claim: customDoubleArray, value: [0.1,1.1,2.2,3.3,4.4]
 Added claim: iss, value: http://io.packt.jwt
 Added claim: groups, value: ["Echoer","Tester","User","group1","group2"]
 Added claim: preferred_username, value: jdoe
 Added claim: customStringArray, value: ["value0","value1","value2"]
 Added claim: aud, value: [s6BhdRkqt3]
 Added claim: upn, value: jdoe@example.com
 Added claim: customInteger, value: 123456789
 Added claim: auth_time, value: 1555688588
 Added claim: customObject, value: {"my-service":{"roles":["role-in-my-service"],"groups":["group1","group2"]},"service-B":{"roles":["role-in-B"]},"service-C":{"groups":["groupC","web-tier"]},"scale":0.625}
 Added claim: exp, value: Fri Apr 19 09:43:08 PDT 2019
 Added claim: customDouble, value: 3.141592653589793
 Added claim: iat, value: Fri Apr 19 08:43:08 PDT 2019
 Added claim: jti, value: a-123
 Added claim: customString, value: customStringValue
eyJraWQiOiJcL3ByaXZhdGUta2V5LnBlbSIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiIyNDQwMDMyMCIsImN1c3RvbUludGVnZXJBcnJheSI6WzAsMSwyLDNdLCJjdXN0b21Eb3VibGVBcnJheSI6WzAuMSwxLjEsMi4yLDMuMyw0LjRdLCJpc3MiOiJodHRwOlwvXC9pby5wYWNrdC5qd3QiLCJncm91cHMiOlsiRWNob2VyIiwiVGVzdGVyIiwiVXNlciIsImdyb3VwMSIsImdyb3VwMiJdLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqZG9lIiwiY3VzdG9tU3RyaW5nQXJyYXkiOlsidmFsdWUwIiwidmFsdWUxIiwidmFsdWUyIl0sImF1ZCI6InM2QmhkUmtxdDMiLCJ1cG4iOiJqZG9lQGV4YW1wbGUuY29tIiwiY3VzdG9tSW50ZWdlciI6MTIzNDU2Nzg5LCJhdXRoX3RpbWUiOjE1NTU2ODg1ODgsImN1c3RvbU9iamVjdCI6eyJteS1zZXJ2aWNlIjp7InJvbGVzIjpbInJvbGUtaW4tbXktc2VydmljZSJdLCJncm91cHMiOlsiZ3JvdXAxIiwiZ3JvdXAyIl19LCJzZXJ2aWNlLUIiOnsicm9sZXMiOlsicm9sZS1pbi1CIl19LCJzZXJ2aWNlLUMiOnsiZ3JvdXBzIjpbImdyb3VwQyIsIndlYi10aWVyIl19LCJzY2FsZSI6MC42MjV9LCJleHAiOjE1NTU2OTIxODgsImN1c3RvbURvdWJsZSI6My4xNDE1OTI2NTM1ODk3OTMsImlhdCI6MTU1NTY4ODU4OCwianRpIjoiYS0xMjMiLCJjdXN0b21TdHJpbmciOiJjdXN0b21TdHJpbmdWYWx1ZSJ9.Tb8Fet_3NhABc6E5z5N6afwNsxzcZaa9q0eWWLm1AP4HPbJCOA14L275D-jAO42s7yQlHS7sUsi9_nWStDV8MTqoey4PmN2rcnOAaKqCfUiLehcOzg3naUk0AxRykCBO4YIck-qqvlEaZ6q8pVW_2Nfj5wZml2uPDq_X6aVLfxjaRzj2F4OoeKGH51-88yeu7H2THUMNLLPB2MY4Ma0xDUFXVL1TXU49ilOXOWTHAo7wAdqleuZUavtt_ZQfRwCUoI1Y-dltH_WtLdjjYw6aFIeJtsyYCXdqONiP6TqOpfACOXbV_nBYNKpYGn4GMiPsxmpJMU8JAhm-jJzf9Yhq6A
[INFO] -----------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] -----------------------------------------------------------------------
[INFO] Total time:  1.328 s
[INFO] Finished at: 2019-04-19T08:43:08-07:00
[INFO] -----------------------------------------------------------------------

这些示例应该能让你对微服务客户端之间的交互以及使用 JWT 来保护微服务端点实现无状态认证和 RBAC(基于角色的访问控制),以及基于 JWT 中声明的定制授权有一个感觉。

总结

在本章中,我们学习了 MicroProfile 健康检查和 JWT 传播项目。现在你应该理解了什么是健康检查以及如何添加应用程序特定的检查,这些检查被称为程序。这允许你的微服务在云环境中描述其非琐碎的健康要求。你也应该理解如何使用 JWT 在微服务之上提供认证和授权能力,以控制对端点的访问。你也应该理解如何使用 JWT 中的内容以用户特定方式增强你的微服务。

下一章将介绍 MicroProfile Metrics 和 OpenTracing 特性。这些特性允许你的微服务提供附加信息...

问题

  1. 马克 wp-hc 协议在所有环境中都有用吗?

  2. 一个 MP-HC 响应可以包含任意属性吗?

  3. 如果我的应用程序有不同的服务类型需要报告健康状态怎么办?

  4. 什么是 JWT(JSON Web Token)?

  5. 什么是声明(claim)?

  6. 对 JWT 中可以有什么内容有限制吗?

  7. 在验证 JWT 时,主要需要经过哪些步骤?

  8. 除了安全注解之外,我们还可以如何使用 JWT 来进行授权检查?

第六章:MicroProfile Metrics 和 OpenTracing

一旦开发人员编写了代码并将其投入生产,就需要观察代码在做什么,表现如何,以及使用了哪些资源。MicroProfile 为解决这些问题创建了两个规范:Metrics 和(与)OpenTracing 的集成。

从 Metrics 部分开始,我们将讨论以下主题:

  • 制定规范的依据

  • 启用服务器上指标的暴露格式

  • 从您的应用程序内部提供指标

  • 使用 Prometheus,一个云原生的时间序列数据库,来检索和分析指标数据

在 OpenTracing 部分,我们将讨论以下内容:

  • 跟踪领域简介

  • 配置属性...

MicroProfile Metrics

MicroProfile Metrics 暴露运行服务器的指标数据(通常称为遥测),例如 CPU 和内存使用情况,以及线程计数。然后,这些数据通常被输入到图表系统中,以随时间可视化指标或为容量规划目的服务;当然,当值超出预定义的阈值范围时,它们也用于通知 DevOps 人员在阈值范围内。

Java 虚拟机长期以来一直通过 MBeans 和 MBeanServer 暴露数据。Java SE 6 见证了所有虚拟机定义如何从远程进程访问 MBean 服务器的(基于 RMI)远程协议的引入。处理此协议是困难的,并且与今天的基于 HTTP 的交互不符。

另一个痛点是许多全球存在的服务器在不同的名称下暴露不同的属性。因此,设置不同类型服务器的监控并不容易。

MicroProfile 通过一个基于 HTTP 的 API,允许监控代理访问,以及一个 Java API,允许在服务器和 JVM 指标之上导出应用程序特定指标,创建了一个监控规范,解决了这两个问题。

MicroProfile Metrics 正在开发 2.x 版本的规范,其中有一些对 1.x 的破坏性变化。以下部分讨论 1.x - 2.0 中的变化在MP-Metrics 2.0 中的新特性部分讨论。

规范定义了指标的三种作用域:

  • 基础:这些指标主要是 JVM 统计数据,每个合规的供应商都必须支持。

  • 供应商:可选的供应商特定指标,这些指标是不可移植的。

  • 应用:来自已部署应用程序的可选指标。Java API 将在提供应用程序特定指标部分中展示。

经典 JMX 方法的另一个问题,MicroProfile Metrics 解决了,就是关于指标语义的元数据信息不足。

元数据

元数据是 MicroProfile Metrics 中的一个非常重要的部分。虽然暴露一个名为foo的指标,其值为142是可能的,但这个指标并不具有自描述性。看到这个指标的运营商无法判断这是关于什么的,单位是什么,以及142是否是一个好值。

元数据用于提供单位和度量的描述,这样前述的现在可以是foo: runtime; 142秒。这现在允许在显示上正确缩放至两分钟和 22 秒。而收到与这个度量相关的警报的用户可以理解它指的是某些运行时计时。

从服务器检索度量

微 Profile 度量通过一个 REST 接口暴露度量指标,默认情况下,在/metrics上下文根下。你可以找到代码在github.com/PacktPublishing/Hands-On-Enterprise-Java-Microservices-with-Eclipse-MicroProfile/tree/master/Chapter05-metrics。按照README.md文件来构建代码,运行它,并用浏览器访问几遍[localhost:8080/book-metrics/hello](http://localhost:8080/book-metrics/hello)[localhost:8080/book-metrics](http://localhost:8080/book-metrics)端点来生成一些数据。

截至 MicroProfile 1.3/2.0,规范中没有关于保护该端点的任何内容。因此留给个别实现自行处理。

使用这个 REST 接口,很容易检索数据,例如,通过以下curl命令:

$ curl http://localhost:8080/metrics

这个命令显示了 Prometheus 文本格式(缩写)中的 Metrics 1.x 输出:

# TYPE base:classloader_total_loaded_class_count counter
base:classloader_total_loaded_class_count 13752.0 
# TYPE base:cpu_system_load_average gauge
base:cpu_system_load_average 2.796875
# TYPE base:thread_count counter
base:thread_count 76.0
# TYPE vendor:memory_pool_metaspace_usage_max_bytes gauge
vendor:memory_pool_metaspace_usage_max_bytes 7.0916056E7
# TYPE application:hello_time_rate_per_second gauge
application:hello_time_rate_per_second{app="shop",type="timer"} 
3.169298061424996E-10
# TYPE application:hello_time_one_min_rate_per_second gauge
application:hello_time_one_min_rate_per_second{app="shop",type="timer"} 0.0
[...]

如果你没有提供媒体类型,默认输出格式是 Prometheus 文本格式(也可以在浏览器中很好地渲染)。Prometheus 格式向值中的# TYPE# HELP行暴露附加元数据。你也可以在前一个示例中看到作用域(基本、供应商和应用程序)是如何被添加到实际度量名称之前的。

另外,通过提供一个HAccept头(再次缩写)来检索 JSON 格式的数据是可能的:

$ curl -HAccept:application/json http://localhost:8080/metrics

这个命令导致以下输出:

{
 "application" :
 {
 "helloTime" : {
 "p50": 1.4884994E7,
 [...]
 "count": 1,
 "meanRate": 0.06189342578194245
 },
 "getCounted" : 1
 },
 "base" :
 {
 "classloader.totalLoadedClass.count" : 13970,
 "cpu.systemLoadAverage" : 2.572265625,
 "gc.PS Scavenge.time" : 290
 },
 "vendor" :
 {
 "test" : 271,
 "memoryPool.Metaspace.usage.max" : 72016928,
 }

在这种情况下,纯净数据被暴露出来;作用域构成了一个顶级层次,相应的度量指标被嵌套在其中。可以通过一个 HTTP XOPTIONS调用检索匹配的元数据:

$ curl XOPTIONS -HAccept:application/json http://localhost:8080/metrics

输出现在包含元数据作为一个映射:

{
"application" : {
 "helloTime": {
 "unit": "nanoseconds",
 "type": "timer",
 "description": "Timing of the Hello call",
 "tags": "app=shop,type=timer",
 "displayName": "helloTime"
 }
}
[...]
}

既然我们已经了解了如何检索不同类型的数据和元数据,我们将快速查看如何限制检索到特定的作用域。

访问特定作用域

通过在路径后附加作用域名称,也可以只检索单个作用域的数据。在以下示例中,我们只检索基本作用域的度量指标:

$ curl http://localhost:8080/metrics/base

现在只显示基本作用域的度量指标:

# TYPE base:classloader_total_loaded_class_count counterbase:classloader_total_loaded_class_count 13973.0# TYPE base:cpu_system_load_average gaugebase:cpu_system_load_average 1.92236328125

在本节中,我们看到了如何从启用了 MicroProfile Metrics 的服务器检索度量。基本和供应商作用域中的度量由服务器预定义。应用程序作用域中的度量可以由用户定义,这是我们将在下一节中探索的内容...

提供应用程序特定的度量

应用程序可以选择通过 CDI 编程模型暴露度量数据。这个模型受到了 DropWizard Metrics 的启发,以便更容易将应用程序过渡到 MP-Metrics。它还使用了来自 DropWizard Metrics 的注解,这些注解已经被增强以支持元数据。

让我们从一个例子开始,定义一个计数器然后在代码中递增:

@Inject
@Metric(absolute = true, description = "# calls to /health")
Counter hCount; // This is the counter

@GET
@Path("/health")
public Response getHealth() throws Exception {
    hCount.inc(); // It is increased in the application
    [...]
}

在这个例子中,我们通过将计数器注入到hCount变量中来注册计数器:

@Metric注解提供了额外信息,例如描述,同时也指出名称是变量名而不是额外的包名(absolute=true)。

在以下示例中,我们让实现来自动计数。这个实现代表了计数一个方法或 REST 端点的常见用例:

@Counted(absolute=true,
        description="# calls to getCounted",
        monotonic=true)
@GET
@Path("/c")
public String getCounted() {
    return "Counted called";
}

@Countedmonotonic属性表示要不断增加计数器,否则当离开方法时它会减少。

更多类型的度量

计数器是唯一可以暴露的度量类型,并且经常需要更复杂的类型,例如,记录方法调用持续时间的分布。

我们快速看一下这些。大多数遵循@Counted的模式。

仪表器

仪表器是一个值任意上升和下降的度量。仪表器总是由一个提供仪表器值的方法支持:

@Gauge
int provideGaugeValue() {
  return 42;  // The value of the gauge is always 42
}

仪表器的值在客户端请求值时计算,就像所有其他值一样。这要求实现仪表器方法非常快,以便调用者不会被阻塞。

仪表器

仪表器测量随着时间的推移被装饰方法调用的速率。对于 JAX-RS 端点,这就是每秒的请求数。可以通过注解声明仪表器:

@GET@Path("/m")@Metered(absolute = true)public String getMetered() {  return "Metered called";}

当客户端请求从仪表器中数据时,服务器提供平均速率,以及一、五、十五分钟的移动平均值。后者对一些读者来说可能熟悉,来自 Unix/Linux 的uptime命令。

直方图

直方图是一种度量类型,它样本数据的分布。它主要用于记录被装饰方法执行所需时间的分布。直方图不能像其他类型那样通过专用注解声明,但例如,计时器包含直方图数据。要单独使用直方图,您需要在代码中注册并更新它:

// Register the Histogram
@Inject
@Metric(absolute = true)
private Histogram aHistogram;

// Update with a value from 0 to 10
@GET
@Path("/h")
public String getHistogram() {
  aHistogram.update((int) (Math.random() * 10.0));
  return "Histogram called";
}

这种在代码中使用度量的方式对其他类型也是可行的。

计时器

计时器基本上是直方图和仪表器的组合,也可以通过注解声明:

@GET@Path("/hello")@Timed(name="helloTime", absolute = true,        description = "Timing of the Hello call",       tags={"type=timer","app=shop"})public String getHelloTimed() {  try {    Thread.sleep((long) (Math.random()*200.0));  } catch (InterruptedException e) {     // We don't care if the sleep is interrupted.  }  return "Hello World";}

这个例子中的代码等待一小段时间的随机量,使输出更有趣。

标记

标签或标签是组织信息的一种方式。这些在 Docker 和 Kubernetes 中变得流行。在 MicroProfile Metrics 1.x 中,它们会被直接传递到输出中,而不会进一步处理,并不能用来区分指标。MicroProfile Metrics 支持服务器级和每个指标的标签,然后会在输出中合并在一起。

服务器级标签

服务器级标签是通过环境变量MP_METRICS_TAGS设置的,如下所示:

export MP_METRICS_TAGS=app=myShopjava -jar target/metrics-thorntail.jar

这些标签将被添加到服务器中定义的所有指标中,并添加到相应的输出格式中。

所以,在之前的命令下,一个名为@Counted(absolute=true) int myCount;的计数器,最终会在 Prometheus 中显示如下:

# TYPE application:my_count counterapplication:my_count{app="myShop"} 0

每个指标的标签

标签也可以基于每个指标提供:

@Counted(tags=[“version=v1”,”commit=abcde”])
void doSomething() {
  [...]
}

这个示例在名为doSomething的指标上定义了两个标签,version=v1commit=abcde。这些将与全局标签合并用于输出。有了之前的全局标签,输出中就会有三个标签。

在本节中,我们了解了如何向指标添加标签以提供附加元数据。这些可以是全局的,适用于服务器暴露的所有指标,也可以是特定于应用程序的,适用于单个指标。

使用 Prometheus 检索指标

既然我们已经了解了暴露的指标以及如何定义我们自己的指标,现在让我们来看看我们如何可以在一个时间序列数据库TSDB)中收集它们。为此,我们使用 Prometheus,一个 CNCF(www.cncf.io/)项目,在云原生世界中得到了广泛采用。

您可以从prometheus.io下载 Prometheus,或者在 macOS 上通过brew install prometheus安装。

一旦下载了 Prometheus,我们需要一个配置文件来定义要抓取的目标,然后可以启动服务器。对于我们来说,我们将使用以下简单的文件:

.Prometheus configuration for a Thorntail Server, prom.ymlscrape_configs:# Configuration to poll from Thorntail- job_name: 'thorntail' ...

新增于 MP-Metrics 2.0

注意:您在读到这些内容时,MicroProfile Metrics 2.0 可能还没有发布,而且内容可能会根据早期用户/实施者的反馈略有变化。

对计数器的更改——引入 ConcurrentGauge

在 Metrics 1.x 中,计数器有两个功能:

  • 为了提供一个并发调用次数的测量指标

  • 作为一个可以计数提交事务数量的指标,例如

不幸的是,当使用没有指定monotonic关键词的注解时,第一种方法是默认的,这是出乎意料的,也让很多用户感到困惑。这种方法的第二个版本也有其问题,因为计数器的值也可以随意减少,这违反了计数器是一个单调递增指标的理解。

因此,度量工作组决定更改计数器的行为,使它们只作为单调递增的指标工作,并将推迟...

标记

标签现在也用于区分具有相同名称和类型但不同标签的指标。它们可以用来支持许多指标result_code在 REST 端点上,以计算(未)成功的调用次数:

@Inject
@Metric(tags="{code,200}", name="result_code")
Counter result_code_200k;

@Inject
@Metric(tags="{code,500}", name="result_code")
Counter result_code_500;

@GET
@Path("/")
public String getData(String someParam) {

 String result = getSomeData(someParam);
 if (result == null ) {
   result_code_500.inc();
 } else {
   result_code_200.inc();
 }
 return result;
}

在底层,指标不再仅按名称和类型进行标识,还按它们的标签进行标识。为此,引入了新的MetricID来容纳名称和标签。

数据输出格式的变化

在 MicroProfile Metrics 2.0 中引入多标签指标需要对提供给客户端的指标数据格式进行更改。

Prometheus 格式也存在一些不一致之处,因此我们决定以有时不兼容的方式重新设计这些格式:

  • 作用域和指标名称之间的冒号(:)分隔符已更改为下划线(_)。

  • Prometheus 输出格式不再需要将 camelCase 转换为 snake_case。

  • 垃圾收集器的基础指标格式已更改,现在使用各种垃圾收集器的标签。

请参阅 MicroProfile 2.0 规范中的发布说明:github.com/eclipse/microprofile-metrics/releases/tag/2.0 ...

MicroProfile OpenTracing

在微服务构成的现代世界中,一个请求可能会穿过在不同机器、数据中心,甚至地理位置上运行的多个进程。

此类系统的可观测性是一项具有挑战性的任务,但当正确实现时,它允许我们讲述每个单独请求的故事,而不是从指标和日志等信号派生出的系统的总体状态。在本章中,我们将向您介绍分布式追踪,并解释 MicroProfile OpenTracing 1.3 中的 OpenTracing 集成。

在前一节中,我们学习了关于指标以及它们如何观察应用程序或每个单独的组件。这些信息无疑非常有价值,并为系统提供了宏观视图,但同时,它很少提到穿越多个组件的每个单独请求。分布式追踪提供了关于请求端到端发生的微观视图,使我们能够回顾性地理解应用程序的每个单独组件的行为。

分布式追踪是基于动作的;换句话说,它记录了系统中的所有与动作相关的信息。例如,它捕获了请求的详细信息及其所有因果相关活动。我们不会详细介绍这种追踪是如何工作的,但简而言之,我们可以得出以下结论:

  • 追踪基础架构为每个请求附加了上下文元数据,通常是唯一标识符集合——traceIdspanIdparentId

  • 观测层记录剖析数据并在进程内部及进程之间传播元数据。

  • 捕获的剖析数据包含元数据和对先前事件的因果引用。

根据捕获的数据,分布式跟踪系统通常提供以下功能:

  • 根本原因分析

  • 延迟优化——关键路径分析

  • 分布式上下文传播——行李

  • 上下文化日志记录

  • 服务依赖分析

在我们深入探讨 MicroProfile OpenTracing 之前,让我们简要地看看 OpenTracing,以便我们能更好地理解它提供的 API 是什么。

OpenTracing 项目

OpenTracing 项目(opentracing.io)提供了一个中立的规范(https://github.com/opentracing/specification)和多语言 API,用于描述分布式事务。中立性很重要,因为在大规模组织中启用分布式跟踪时,代码 instrumentation 是最耗时和最具挑战性的任务。我们想强调的是 OpenTracing 只是一个 API。实际部署将需要一个运行在监控进程内部的 plugged 跟踪器实现,并将数据发送到跟踪系统。

从 API 角度来看,有三个关键概念:跟踪器、跨度、和跨度上下文。跟踪器是应用程序中可用的单例对象,可以用来建模一个...

配置属性

OpenTracing 是中立且可以与使用此 API 的任何供应商的跟踪实现配合使用。每个跟踪器实现将配置不同。因此,配置超出了 MicroProfile OpenTracing 规范的范围。然而,规范本身暴露了几个配置属性,以调整跟踪范围或生成数据。配置利用了 MicroProfile Config 规范,为所有支持的配置选项提供了一种一致的方法。

目前,规范暴露了以下内容:

  • mp.opentracing.server.skip-pattern:一个跳过模式,用于避免跟踪选定的 REST 端点。

  • mp.opentracing.server.operation-name-provider:这指定了服务器跨度操作名称提供程序。可能的值有http-pathclass-method。默认值是class-method,它完全使用一个限定类名与方法名拼接;例如,GET:org.eclipse.Service.gethttp-path使用@Path注解的值作为操作名称。

自动 instrumentation

这里的动机是自动捕获所有关键性能信息,并在运行时之间自动传播跟踪上下文。第二部分尤其重要,因为它确保了跟踪不会中断,我们能够调查端到端的调用。为了成功跟踪,必须在运行时之间的每种通信技术上进行 instrumentation。在 MicroProfile 的情况下,是 JAX-RS 和 MicroProfile Rest Client。

JAX-RS

微 Profile OpenTracing 自动追踪所有入站的 JAX-RS 端点。然而,JAX-RS 客户端一侧更加复杂,需要调用注册 API,org.eclipse.microprofile.opentracing.ClientTracingRegistrar.configure(ClientBuilder clientBuilder),以添加追踪能力。微 Profile 实现可以为所有客户端接口全局启用追踪;然而,建议使用注册 API 以获得更好的可移植性。

可以通过禁用特定请求的追踪或更改生成的服务器跨度的操作名称来修改默认的追踪行为。有关更多信息,请在本章后面的配置属性部分查阅。instrumentation 层自动向每个跨度添加以下请求范围的信息:

  • http.method:请求的 HTTP 方法。

  • http.status_code:请求的状态代码。

  • http.url:请求的 URL。

  • component:被 instrumented 组件的名称,jaxrs

  • span.kind:客户端或服务器。

  • errortruefalse。这是可选的,如果存在,instrumentation 还将在跨度日志中添加一个异常作为 error.object

所有这些标签都可以用于通过追踪系统用户界面查询数据,或者它们可以用于许多追踪系统提供数据分析作业。可以通过注入的追踪器实例向当前活动的跨度添加额外元数据。这可以在过滤器中全局执行或在 rest 处理程序中局部执行,如下面的代码示例所示,通过向服务器跨度添加用户代理头(1):

@Path("/")
public class JaxRsService {
   @Inject
   private io.opentracing.Tracer tracer;

   @GET
   @Path("/hello")
   @Traced(operationName="greeting") (2)
   public String hello(@HeaderParam("user-agent") String userAgent) {
       tracer.activeSpan().setTag("user-agent", userAgent); (1)
   }
}

默认情况下,服务器端跨度操作名称为 http_method:package.className.method。然而,这可以通过使用 @Traced 注解(2)或通过配置属性(参考配置部分)在本地或全局更改。

微 Profile Rest Client

如前一部分所述,所有 REST 客户端接口默认情况下都会自动追踪,无需额外的配置。要更改此行为,请将 @Traced 注解应用于接口或方法以禁用追踪。当应用于接口时,所有方法都将从追踪中跳过。请注意,追踪上下文不会被传播。因此,如果请求继续到 instrumented runtime,将开始新的追踪。

显式 instrumentation

有时,自动 instrumentation 并没有捕获所有关键的计时信息,因此需要额外的追踪点。例如,我们希望追踪业务层的调用或初始化由 OpenTracing 项目提供的三方 instrumentation(github.com/opentracing-contrib)。

显式地 instrumentation 可以通过三种方式进行:

  • 上下文和依赖注入CDI)bean 上添加 @Traced 注解。

  • 注入追踪器并手动创建跨度。

  • 初始化第三方仪器。外部仪器的初始化取决于其自己的初始化要求。MicroProfile 只需要提供一个跟踪器实例,这在之前的要点中已经涵盖。

让我们现在详细讨论这些内容。

@Traced 注解

MicroProfile OpenTracing 定义了一个@Traced注解,可以用于启用 CDI 豆的跟踪,或禁用自动跟踪接口的跟踪。该注解还可以用于重写其他自动跟踪组件的操作名称——JAX-RS 端点。

下面的代码示例显示了如何使用@Traced注解来启用 CDI 豆的跟踪。(1)为豆定义的所有方法启用跟踪。(2)重写了默认操作名称(package.className.method)为get_all_users(3)禁用了健康方法的跟踪:

@Traced (1)@ApplicationScopedpublic class Service {   @Traced(operationName = "get_all_users") (2)   public void getUsers() {        // business code   } @Traced(false) (3) ...

跟踪器注入

应用程序可以注入一个io.opentracing.Tracer豆,暴露出完整的 OpenTracing API。这允许应用程序开发者利用更高级的使用案例,例如向当前活动的跨度添加元数据,手动创建跨度,使用行李进行上下文传播,或初始化额外的第三方仪器。

下面的代码显示了如何使用跟踪器将数据附加到当前活动的跨度,(1)

@Path("/")
public class Service {
    @Inject
    private Tracer tracer;

    @GET
    @Path("")
    @Produces(MediaType.TEXT_PLAIN)
    public String greeting() {
       tracer.activeSpan()
           .setTag("greeting", "hello"); (1)
       return "hello";
   }
}

这可以用于向跨度添加业务相关数据,但也用于记录异常或其他分析信息。

使用 Jaeger 进行跟踪

到目前为止,我们只谈论了仪器仪表的不同方面。然而,要运行完整的跟踪基础设施,我们需要一个跟踪后端。在本节中,我们将使用 Jaeger(www.jaegertracing.io/)来展示收集的跟踪数据如何在跟踪系统中呈现。我们选择 Jaeger 是因为 Thorntail 提供了与 Jaeger 的直接集成。其他供应商可以提供与其他系统的集成,例如 Zipkin 和 Instana。几乎每个跟踪系统都提供了一个类似于甘特图的视图(或时间线)来查看跟踪。这种视图对于跟踪新手来说可能有些令人不知所措,但它是一个分析分布式系统中调用的系统化工具。

下面的屏幕快照显示了...

总结

在本章中,我们学习了关于服务器和应用程序的可观测性。

指标,或遥测,可以帮助确定服务器或应用程序的性能特性。MicroProfile 通过 Metrics 规范提供了一种以标准化方式导出指标的方法。应用程序编写者可以使用 MicroProfile Metrics 将他们的数据以注解或通过调用 Metrics API 的方式装饰性地暴露给监控客户端。

本章进一步解释了 MicroProfile 中 OpenTracing 集成如何为通过系统的每个单独事务提供端到端的视图。我们讨论了配置属性,展示了 JAX-RS 的跟踪,最后调查了 Jaeger 系统中的数据。

在下一章,我们将学习如何通过 OpenAPI 文档化(REST)API,并通过类型安全的 REST 客户端调用这些 API。

问题

  1. 分布式追踪和指标之间的区别是什么?

  2. 分布式追踪系统通常提供哪些功能?

  3. 在 MicroProfile OpenTracing 中,系统哪些部分会自动被追踪?

  4. MicroProfile OpenTracing 为每个 REST 请求添加了哪些标签?

  5. 如何在业务代码中添加显式 instrumentation?

  6. Metrics 中的作用域是什么,它们的理由是什么?

  7. 什么决定了 REST 请求到 Metrics API 的输出格式?

  8. 用户应用程序中可用的哪些方法可以导出指标?

第七章:MicroProfile OpenAPI 和类型安全的 REST 客户端

Eclipse MicroProfile 为 Java 微服务提供了一系列丰富的规范。其中两个,Eclipse MicroProfile OpenAPI 和 Eclipse MicroProfile REST Client,分别帮助您微服务的 API 文档化,并为 REST 端点提供类型安全的调用 API。OpenAPI 简化了微服务端点的文档化,并使此元数据可供第三方开发者查阅。类型安全的 REST 客户端简化了对象到 HTTP 或 JSON 的编解码。

本章将涵盖以下主题:

  • 每个这些规范提供的能力。

  • 一些这些能力的简单代码示例

  • 如何获取关于这些规范的更多信息

MicroProfile OpenAPI 及其能力的介绍

推动数字经济的移动力量导致企业需要建立一个全渠道开发方法,以优化成本、提高效率和改善客户体验。这种方法的促进者是 API,这导致了 API 经济和 API 引导或 API 优先的开发实践等概念。此外,微服务架构已成为现代开发的架构选择。微服务之间的 API(即 RESTful)通信已被采用为事实上的标准,因为它与微服务的智能端点和大管道去中心化治理去中心化数据管理特征相契合。

然而,随着微服务数量的增加,微服务架构的管理可能会变得难以控制。然而,您可以通过 API 管理您的微服务。您可以将管理、安全、负载均衡和节流策略应用于面向您的微服务的 API。

Eclipse MicroProfile OpenAPI 为开发者提供 Java 接口,用于从他们的 Java RESTful Web Services (JAX-RS)应用程序生成 OpenAPI v3 文档。规范要求在根 URL /openapi 上提供一个完全处理的 OpenAPI 文档,作为 HTTP GET操作,如下所示:

GET http://myHost:myPort/openapi

所需的协议是http。然而,规范的实现者强烈鼓励也支持https协议,以便安全地连接到 OpenAPI 端点。

OpenAPI 文档是从以下三个来源创建的。这三个来源(在本章后面的部分中介绍)如下:

  • 通过处理应用程序中发现的 JAX-RS 注解(和可选的 OpenAPI 注解)生成。

  • 通过提供一个实现OasModelReader的 Java 类,应用程序程序化地构建。

  • 应用程序部署中包含的静态 OpenAPI 文档。

这三个来源(任意组合)结合产生一个 OpenAPI 文档,该文档可以通过提供实现OasFilter接口的 Java 类进行过滤,然后在前面的/openapi端点提供服务。

配置

MicroProfile OpenAPI 规范利用 MicroProfile 配置规范来配置其参数和值。例如,用于注入配置值,MicroProfile OpenAPI 可以使用默认和自定义 ConfigSources。

关于 ConfigSources 的更多信息,你可以访问github.com/eclipse/microprofile-config/blob/master/spec/src/main/asciidoc/configsources.asciidoc

有很多可配置的项目。以下表格包含它们的子集:

配置项 描述
mp.openapi.scan.disable 禁用注解扫描的配置属性。默认值是false
mp.openapi.servers 指定全局服务器列表的配置属性,用于...

生成 OpenAPI 文档

如前所述,MicroProfile OpenAPI 规范要求从三个来源的组合生成 OpenAPI 文档。

然后你有几个选择:

  • 使用 MicroProfile OpenAPI 注解扩展由 JAX-RS 注解生成的 OpenAPI 文档。

  • 利用从/openapi的初始输出,你可以将其作为参考开始记录你的 API。在这种情况下,你可以在编写任何代码之前编写静态 OpenAPI 文件(在本章后面的部分介绍),这是组织通常采用的方法来锁定 API 的合同,即它是 API 优先的开发实践。

  • 通过编程使用编程模型来启动或完成 OpenAPI 模型树。这部分内容将在本章后面介绍。

此外,你可以使用一个过滤器在构建 OpenAPI 模型后更新它。

MicroProfile OpenAPI 注解

可能是 OpenAPI 信息最常见来源的是组成标准 JAX-RS 应用程序定义的一组注解。这些注解,加上由 MicroProfile OpenAPI 规范定义的额外(可选)注解,可以被 MicroProfile 平台扫描和处理,以产生一个 OpenAPI 文档。

MP OpenAPI 规范要求从纯 JAX-RS 2.0 应用程序生成有效的 OpenAPI 文档。如果你对 OpenAPI 不熟悉,你可以简单地将你的现有 JAX-RS 应用程序部署到 MicroProfile OpenAPI 运行时,并查看/openapi的输出。

为了填写生成的 OpenAPI 文档的额外详细信息,你可以进一步注解你的...

使用示例

以下是 MicroProfile OpenAPI 注解的一些使用示例:

示例 1 – 简单操作描述(缩写):

@GET
@Path("/findByMake")
@Operation(summary = "Finds cars by make",
           description = "Find cars by their manufacturer")
public Response findCarsByMake(...)
{ ... }

以下是示例 1 的输出:

/car/findByMake:
 get:
 summary: Finds cars by make
 description: Find cars by their manufacturer

示例 2 – 具有不同响应的操作(简化):

@GET
@Path("/{name}")
@Operation(summary = "Get customer by name")
  @APIResponse(description = "The customer",
             content = @Content(mediaType = "application/json",
                                schema = @Schema(implementation = Customer.class))),
@APIResponse(responseCode = "400", description = "Customer not found")
public Response getCustomerByName(
        @Parameter(description = "The name of the customer to be fetched", required = true) @PathParam("name") String name)

{...}

以下是示例 2 的输出:

/customer/{name}:
 get:
 summary: Get customer by name
 operationId: getCutomerByName
 parameters:
 - name: name
 in: path
 description: 'The name of the customer to be fetched'
 required: true
 schema:
 type: string
 responses:
 default:
 description: The customer
 content:
 application/json:
 schema:
 $ref: '#/components/schemas/Customer'
 400:
 description: Customer not found

更多示例,请参考 MicroProfile OpenAPI 规范的 wiki 页面:github.com/eclipse/microprofile-open-api/wiki

静态 OpenAPI 文件

如本章前面提到的,静态 OpenAPI 文件是创建 OpenAPI 文档的三个来源之一。在下面,我们给你一个简短的介绍,告诉你如何生成一个以及如何将其包含在你的部署中。许多组织使用 API 优先的开发实践,这涉及到在为它们实现任何代码之前,甚至定义静态 OpenAPI 文件。

首先,你可以通过使用开源编辑器如 Swagger Editor(editor.swagger.io)来创建一个 OpenAPI 文档。下面是一个显示这个过程的屏幕截图:

使用这个编辑器,你可以从样本...开始。

编程模型

你可以通过使用 MicroProfile OpenAPI 编程模型,通过 Java POJOs(Plain Old Java Objects)提供 OpenAPI 元素。完整的模型集在org.eclipse.microprofile.openapi.models包中描述。你可以在github.com/eclipse/microprofile-open-api/tree/master/api/src/main/java/org/eclipse/microprofile/openapi/models了解更多。

你可以使用OASFactory创建一个 OpenAPI 树。以下是一个示例的代码块:

OASFactory.createObject(Info.class).title("Weather")
          .description("Weather APIs").version("1.0.0");

为了引导 OpenAPI 模型树,你可以使用OASModelReader接口。然后,你可以创建这个接口的一个实现,并使用mp.openapi.model.reader配置键进行注册。

以下是全局地在META-INF/microprofile-config.properties中它的定义示例:

mp.openapi.model.reader=com.mypackage.MyModelReader

与静态文件类似,模型读取器可以用来提供完整的或部分的模型树。要提供一个完整的 OpenAPI 模型树,你应该将mp.openapi.scan.disable配置设置为true。否则,将假设这是部分模型。

使用过滤器进行更新

要更新或删除 OpenAPI 文档的某些元素和字段,你可以使用一个过滤器。OASFilter(github.com/eclipse/microprofile-open-api/blob/master/api/src/main/java/org/eclipse/microprofile/openapi/OASFilter.java)接口允许你为各种 OpenAPI 元素接收回调。它允许你覆盖你关心的方法。你可以创建这个接口的一个实现,并使用mp.openapi.filter配置键来注册它。

下面是在META-INF/microprofile-config.properties中其定义的样子:

mp.openapi.filter=com.mypackage.MyFilter

已注册的过滤器对每个模型元素调用一次。例如,filterPathItem方法是...

介绍 MicroProfile REST Client 及其功能

MicroProfile REST ClientMP-RC)提供了一个 API,用于对 REST 端点的类型安全调用。它可以被应用程序用来执行对其他服务的远程调用。

它利用 Java 接口上的 JAX-RS 注解来描述与远程服务实际的合同。这些接口随后被用来创建客户端代理,隐藏了大部分底层的 HTTP 通信。

MP-RC 规范定义了在 Java 接口上使用 JAX-RS 注解的要求,以及 MP-RC 特定的注解来增强行为,包括如何传播进入请求头,如何使用提供者增强 JAX-RS 行为,异常映射,CDI 支持,与其他 MicroProfile 规范的集成。我们从定义一个类型安全的端点接口开始,更详细地查看 MP-RC。

定义端点接口

为了定义一个类型安全的端点接口,我们创建一个 Java 接口,利用 JAX-RS 注解将接口方法映射到它们代理的 REST 端点。一个基本的示例在下述的WorldClockApi接口中说明:

package io.pckt.restc.contract;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.PathParam;import javax.ws.rs.Produces;import javax.ws.rs.core.MediaType;@Path("/api/json")public interface WorldClockApi { static final String BASE_URL = "http://worldclockapi.com/api/json"; @GET @Path("/utc/now") @Produces(MediaType.APPLICATION_JSON) Now utc(); @GET @Path("{tz}/now") @Produces(MediaType.APPLICATION_JSON) Now tz(@PathParam("tz") String tz);}public class Now ...

MicroProfile REST Client 编程 API 使用

MP-RC 支持编程查找和 CDI 注入两种使用方法。以下是一个使用org.eclipse.microprofile.rest.client.RestClientBuilder的 REST 服务示例,它创建了一个类型安全的客户端,用于WorldClockApi接口,作为WorldClockUser.java列出:

package io.pckt.restc.contract;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/api")
@ApplicationScoped
public class WorldClockUser {
 @GET
 @Path("/now-utc")
 @Produces(MediaType.TEXT_PLAIN)
 public String getCurrentDateTime() {
 WorldClockApi remoteApi = RestClientBuilder.newBuilder()
 .baseUri(URI.create(WorldClockApi.BASE_URL))
 .build(WorldClockApi.class);
 Now now = remoteApi.utc();
 return now.getCurrentDateTime();
 }
}

baseUri()方法用于指定与WorldClockApi方法路径解析相对的服务器 URI。build()方法接收要构建的类型安全客户端的 Java 接口。RestClientBuilder的其他方法包括以下内容:

  • baseUrl(URL):与baseUri类似,但接受java.net.URL类型。

  • connectTimeout(long timeout, TimeUnit unit):等待连接到远程服务器的时长。值为 0 表示需要无限等待。

  • readTimeout(long timeout, TimeUnit unit):在远程服务器连接的读取上等待的时间量。0 的值表示需要无限等待。

  • executorService(ExecutorService executor):用于异步请求。我们将在异步部分回到这个。

MicroProfile REST Client CDI 使用

MP-RC 类型安全的接口可以作为 CDI bean 注入。运行时必须为每个用@RegisterRestClient注解标记的接口创建一个 CDI bean。CDI 客户端注入创建的 bean 将包括一个限定符,@RestClient,以区分作为 MP-RC 注入点的使用。以下是我们WorldClockApi接口的更新示例,使用了@RegisterRestClient注解:

import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.PathParam;import javax.ws.rs.Produces;import javax.ws.rs.core.MediaType;import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;@Path("/api/json")@RegisterRestClient()public interface WorldClockApi { static final String BASE_URL = "http://worldclockapi.com/api/json"; ...

MicroProfile Config 集成

对于 CDI 定义的接口,可以使用 MicroProfile Config 属性来定义通过RestClientBuilder API 可用的附加行为。给定我们的io.pckt.restc.contract.WorldClockApi接口,以下 MicroProfile Config 属性可用于控制生成的代理行为:

  • io.pckt.restc.contract.WorldClockApi/mp-rest/url:用于此服务的基 URL,相当于RestClientBuilder#baseUrl方法。

  • io.pckt.restc.contract.WorldClockApi/mp-rest/scope:用于注入的 CDI 作用域的全限定类名;默认为javax.enterprise.context.Dependent

  • io.pckt.restc.contract.WorldClockApi/mp-rest/providers:一个由逗号分隔的全限定提供者类名列表,用于包含在客户端中,相当于RestClientBuilder#register方法或@RegisterProvider注解。

  • io.pckt.restc.contract.WorldClockApi/mp-rest/providers/com.mycompany.MyProvider/priority:这将覆盖com.mycompany.MyProvider提供者在此接口上的优先级。

  • io.pckt.restc.contract.WorldClockApi/mp-rest/connectTimeout:等待连接到远程端点的超时时间,以毫秒为单位。

  • io.pckt.restc.contract.WorldClockApi/mp-rest/readTimeout:等待远程端点响应的超时时间,以毫秒为单位。

简化配置键

由于默认的 MP Config 属性名由于包含接口包名而可能较长,MP-RC 规范支持使用@RegisterRestClient注解的configKey属性来简化属性名前缀:

@Path("/api/json")@RegisterRestClient(baseUri = WorldClockApi.BASE_URL, configKey = "worldClock")public interface WorldClockApi { static final String BASE_URL = "http://worldclockapi.com/api/json";...}

使用worldClock配置键,之前的属性名列表简化为以下内容:

  • worldClock/mp-rest/url

  • worldClock/mp-rest/uri

  • worldClock/mp-rest/scope

  • worldClock/mp-rest/providers

  • worldClock/mp-rest/providers/com.mycompany.MyProvider/priority ...

处理客户端头

假设您想要在 HTTP 授权头中为安全的远程服务指定凭据,但不想在客户端接口方法中有一个authHeader字符串参数。MP-RC 的@ClientHeaderParam注解可以用来指定应该发送而不改变客户端接口方法签名的 HTTP 头部。

以下示例展示了使用@ClientHeaderParam注解提供User-Agent HTTP 头的WorldClockApi接口的两种用法:

WorldClockApiWithHeaders.java
public interface WorldClockApiWithHeaders {
 static final String BASE_URL = "http://worldclockapi.com/api/json";

 default String lookupUserAgent() {
 Config config = ConfigProvider.getConfig();
 String userAgent = config.getValue("WorldClockApi.userAgent", String.class);
 if(userAgent == null) {
 userAgent = "MicroProfile REST Client 1.2";
 }
 return userAgent;
 }

 @GET
 @Path("/utc/now")
 @Produces(MediaType.APPLICATION_JSON)
 @ClientHeaderParam(name = "User-Agent", value = "{lookupUserAgent}")
 Now utc();

 @GET
 @Path("{tz}/now")
 @Produces(MediaType.APPLICATION_JSON)
 @ClientHeaderParam(name = "User-Agent", value = "MicroProfile REST Client 1.2")
 Now tz(@PathParam("tz") String tz);
}

还可以使用ClientHeadersFactory实现批量添加或传播头部:

package org.eclipse.microprofile.rest.client.ext;

public interface ClientHeadersFactory {
 MultivaluedMap<String, String> update(
    MultivaluedMap<String, String> incomingHeaders,
    MultivaluedMap<String, String> clientOutgoingHeaders);
}

在前面的代码片段中,incomingHeadersclientOutgoingHeaders参数的使用方式如下:

  • incomingHeaders:表示入站请求的头部映射

  • clientOutgoingHeaders:代表在客户端接口上指定的只读头部值映射,是@ClientHeaderParam@HeaderParam等头部值的并集

update方法应该返回一个MultivaluedMap,其中包含要与clientOutgoingHeaders映射合并的头部,以便将完整的头部映射发送到出站请求。提供者(如过滤器、拦截器和消息体写入器)仍然可以在发送 HTTP 请求之前修改最终的头部映射。

要启用ClientHeadersFactory,客户端接口必须用@RegisterClientHeaders注解标注。如果此注解指定了一个值,客户端实现必须调用指定ClientHeadersFactory实现类的实例。如果没有指定值,那么客户端实现必须调用DefaultClientHeadersFactoryImpl。这个默认工厂将把从入站 JAX-RS 请求中指定的头部传播到出站请求——这些头部是用逗号分隔的列表在 MicroProfile Config 属性org.eclipse.microprofile.rest.client.propagateHeaders中指定的。

高级用法的提供商注册

RestClientBuilder接口扩展了来自 JAX-RS 的Configurable接口,允许用户在构建过程中注册自定义提供商。支持的提供者的行为由 JAX-RS 客户端 API 规范定义。MP-RC 实现将支持 JAX-RS 的ClientResponseFilterClientRequestFilterMessageBodyReaderMessageBodyWriterParamConverterReaderInterceptorWriterInterceptor

对于具有ClientRequestContext参数的filter方法的ClientResponseFilterClientRequestFilter接口,MP-RC 实现添加了一个名为org.eclipse.microprofile.rest.client.invokedMethod的属性,其值是一个java.lang.reflect.Method对象...

提供商优先级

提供者可以通过注解和RestClientBuilder进行注册。通过构建器注册的提供者将优先于@RegisterProvider注解。@RegisterProvider注解的优先级值优先于类上的任何@javax.annotation.Priority注解。使用RestClientBuilder接口上的注册方法可以覆盖提供者优先级,因为它允许优先级设置。

特性注册

如果注册的提供者类型是 JAX-RS特性,那么该特性设置的优先级也将作为构建器的一部分。实现维护注册提供者的总体优先级,无论它们是如何注册的。特性用于在运行时注册附加提供者,可以通过@RegisterProvider、配置或通过RestClientBuilder进行注册。特性将立即执行。因此,不考虑其优先级(特性总是执行)。

默认提供者

MP-RC 实现必须提供一组最小提供者,包括以下内容:

  • */json类型:

    • JSON-P, javax.json.JsonValue

    • JSON-B, javax.json.bind

  • *类型:

    • byte[]

    • java.lang.String

    • java.io.InputStream

    • java.io.Reader

  • text/plain类型:

    • java.lang.Number 和其子类型

    • int, long, float 和 double

    • java.lang.Character 和 char

    • java.lang.Boolean 和 boolean

异常映射

MP-RC 为通过org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper接口将调用响应映射到异常提供支持:

import javax.annotation.Priority;import javax.ws.rs.Priorities;import javax.ws.rs.core.MultivaluedMap;import javax.ws.rs.core.Response;import java.util.Optional;public interface ResponseExceptionMapper<T extends Throwable> {    int DEFAULT_PRIORITY = Priorities.USER;    T toThrowable(Response response);    default boolean handles(int status, MultivaluedMap<String, Object> headers) {        return status >= 400;    }    default int getPriority() {        return Optional.ofNullable(getClass().getAnnotation(Priority.class))            .map(Priority::value)            .orElse(DEFAULT_PRIORITY);    }}

考虑以下情况...

默认异常映射

每个实现提供了一个默认的ResponseExceptionMapper实现,当响应状态码 >= 400 时,它会将响应映射并调用javax.ws.rs.WebApplicationException。它的优先级为Integer.MAX_VALUE,旨在用作遇到错误时的回退。此映射器默认注册到所有客户端接口,但可以通过将 MP 配置属性microprofile.rest.client.disable.default.mapper设置为true来禁用它。它还可以通过在构建客户端时使用相同属性来在每个客户端上禁用:

RestClientBuilder.newBuilder().property("microprofile.rest.client.disable.default.mapper",true)

异步支持

MP-RC 支持异步方法调用。当客户端接口方法返回类型为java.util.concurrent.CompletionStage<?>类型时,该方法是异步的。WorldClockApi接口的异步方法声明的替代版本,称为WorldClockApiAsync.java,如下所示:

import java.util.concurrent.CompletionStage;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.PathParam;import javax.ws.rs.Produces;import javax.ws.rs.core.MediaType;@Path("/api/json")public interface WorldClockApiAsync { String BASE_URL = "http://worldclockapi.com/api/json"; @GET @Path("/utc/now") @Produces(MediaType.APPLICATION_JSON) CompletionStage<Now> utc(); @GET @Path("{tz}/now") @Produces(MediaType.APPLICATION_JSON) ...

总结

在本章中,我们学习了两个 Eclipse MicroProfile 规范,分别是 Eclipse MicroProfile OpenAPI 和 Eclipse MicroProfile REST Client。前者提供了一个规范,用于生成符合 OpenAPI 的微服务文档,后者提供了一个规范,用于以类型安全的方式调用 REST 端点。在本章中,我们介绍了这些规范的具体功能,提供了一些示例代码,并指出了如何获取关于这些规范的更多信息。您已经学习了 Eclipse MicroProfile OpenAPI 和 Eclipse MicroProfile REST Client 规范的功能和能力,如何使用它们的注解和程序化接口,以及如何将它们集成到您的应用程序中。

在下一章中,我们将讨论并深入研究市场上目前存在的 Eclipse MicroProfile 的开源实现。

问题

  1. 您需要对提供给/openapi端点的信息做任何事情吗?

  2. 我能否仅通过添加一两个额外的注解来增强 OpenAPI 输出?

  3. 使用静态 OpenAPI 文件有什么意义?

  4. 我是否需要我想要使用的 REST 端点微服务来提供 MP-REST 接口?

  5. 您如何为类型安全的接口外部化基础 URL?

  6. 如果需要传播传入请求头,该怎么办?

第三部分:MicroProfile 实现和路线图

本节将概述当前市场的实施情况以及未来项目的可能路线图。

本节包含以下章节:

  • 第七章,MicroProfile 实现、Quarkus 和通过会议应用程序实现的互操作性

第八章:MicroProfile 实现、Quarkus 以及通过会议应用程序实现互操作性

Eclipse MicroProfile 的好处之一是它提供了一个规范,使得许多实现之间可以相互操作。这个好处激励了许多供应商和社区组织将 Eclipse MicroProfile 规范作为开源实现。目前市场上共有八个 Eclipse MicroProfile 实现,第九个实现者是 Quarkus。

本章将涵盖以下主题:

  • 对 Eclipse MicroProfile 的八个实现以及如何找到每个实现的进一步信息的描述

  • 如何为这些实现中的每一个生成 Eclipse MicroProfile 示例代码...

当前 MicroProfile 实现

截至编写本书时,共有八个 Eclipse MicroProfile 实现,所有这些都是开源的。以下是这些实现的表格:

开源项目基础 项目位置 支持供应商
Thorntail (thorntail.io/) github.com/thorntail/thorntail Red Hat
Open Liberty (openliberty.io/) github.com/openliberty IBM
Apache TomEE (tomee.apache.org/) github.com/apache/tomee Tomitribe
Payara Micro (www.payara.fish/payara_micro) github.com/payara/Payara Payara Services Ltd.
Hammock (hammock-project.github.io/) github.com/hammock-project Hammock
KumuluzEE (ee.kumuluz.com/) github.com/kumuluz KumuluzEE
启动器 (github.com/fujitsu/launcher) github.com/fujitsu/launcher Fujitsu
Helidon (helidon.io/#) github.com/oracle/helidon Oracle

这些实现中有一些是基于应用程序服务器的,如 Payara 和 Open Liberty,而其他则是基于应用程序组装器,只包括应用程序需要的功能,而不是要求运行一个应用程序服务器,并且通常生成可执行 JAR。然而,基于应用程序服务器的实现也具备生成可执行 JAR 的能力。

应用程序组装器可以生成一个uberjar,一个自包含的可运行 JAR 文件,或者一个将其运行时依赖位于子目录中的应用程序 jar,例如,伴随的liblibs子目录。

符合 Eclipse MicroProfile 标准的实现通过整个伞状发布版本的测试兼容性套件TCK),或者特定版本的 MicroProfile API 的实现,列表可在wiki.eclipse.org/MicroProfile/Implementation找到。目前,这个列表采用荣誉制度,因为它不需要证明 TCK 的结果;它只需要发布者声明他们的实现已经通过了 TCK。

该项目还有一个网站,组织/团体可以自行加入 MicroProfile 生产部署列表。这个列表可以在wiki.eclipse.org/MicroProfile/Adoptions找到。

在下一节中,我们提供了这些实现简要介绍以及如何获取关于它们的更多信息。

Thorntail

红帽公司是开源 Thorntail 项目的赞助商,该项目实现了 Eclipse MicroProfile 规范。Thorntail 是一个应用程序组装器,它只包含您的应用程序所需的服务器运行时组件,并创建一个可执行的 JAR(即 uberjar),您可以通过调用以下命令来执行:

$ java -jar <executable JAR file>

不仅 Thorntail 符合 MicroProfile,它还可以在您的应用程序中包含超出 MicroProfile 的功能。它有一个分数的概念,这是一个包含您想要包含在应用程序中的功能的特定库。分数作为您应用程序的 Maven POM 文件的一个依赖项。超出 MicroProfile ...

Open Liberty

IBM 是开源 Open Liberty 项目的赞助商,该项目实现了 Eclipse MicroProfile 规范。Open Liberty 是 IBM WebSphere Liberty 应用服务器的上游开源项目。Open Liberty 是一个能够生成 uberjar 的应用服务器,其中包含您的应用程序以及内嵌的 Open Liberty 服务器。要运行 uberjar,您需要输入以下命令:

$ java -jar <executable JAR file>

此命令将把 JAR 文件解压到您的用户名临时目录中,然后从那里执行应用程序。

确保 JAR 文件路径中没有空格,否则启动过程将失败。

生成的 uberjar 只能包含server.xml文件中包含的功能的子集。要使用这些最小功能集构建 uberjar,您需要在运行 Maven 时使用minify-runnable-package配置文件。

Open Liberty 文档非常全面,充满了指南和参考文献。

您可以在openliberty.io/docs/找到 Open Liberty 文档。

在他们的文档中,他们有一个专门介绍 MicroProfile 指南的部分,提供了文档齐全的教程。

Apache TomEE

托米部落(Tomitribe)是开源 TomEE 项目的赞助商,该项目实现了 Eclipse MicroProfile 规范。Apache TomEE 是由 Apache Tomcat 组装而成,增加了 Java EE 特性。TomEE 是 Java EE 6 Web Profile 认证的。正如其 GitHub 所描述的,Apache TomEE 是一个轻量级但功能强大的 Java EE 应用服务器,拥有丰富的功能工具。您可以下载几个不同版本的 TomEE,例如 TomEE、TomEE+、TomEE WebApp,但我们感兴趣的是 TomEE MicroProfile。对于 MicroProfile,TomEE 为您生成了一个 uberjar,您可以像以下这样运行:

$ java -jar <executable JAR file>

尽管 TomEE MicroProfile 文档不多,但有一套详尽的...

帕雅拉(Payara)

帕雅拉(Payara)是开源 Payara Micro 项目的赞助商,该项目实现了 Eclipse MicroProfile 规范。Payara 服务器基于开源应用服务器 GlassFish。Payara Micro 是基于 Payara Server 的一个精简版本。正如他们的网站所描述的,Payara Micro 是 Payara Server 的适用于微服务的版本

Payara Micro 的工作方式是 Payara Micro 实例启动,然后将 MicroProfile 微服务作为 WAR 文件部署到其中。例如,要启动一个 Payara Micro 实例,您将输入以下命令:

$ java -jar payara-micro.jar

要启动 Payara Micro 实例并将您的应用程序部署到其中,您将输入以下命令:

$ java -jar payara-micro.jar --deploy <WAR file>

Payara Micro 支持 Java EE 应用程序部署,并且与 Eclipse MicroProfile 兼容。

对于 Payara Micro 文档,请参考docs.payara.fish/documentation/payara-micro/payara-micro.html

最后,Payara Micro 通过使用第三方内存内数据网格产品支持自动集群。

吊床

约翰·阿门特(John Ament)是开源 Hammock 项目的赞助商,该项目实现了 Eclipse MicroProfile 规范。与 Thorntail 相似,Hammock 是一个应用程序组装器,生成 uberjars。要运行 uberjar,您需要输入以下命令:

$ java -jar <executable JAR file>

吊床是一个有观点的微服务框架,用于构建应用程序。它是一个基于 CDI 的框架,意味着它是基于 CDI 容器的,CDI 基于的 bean 在其中运行。它支持两种 CDI 实现(JBoss Weld 和 Apache OpenWebBeans),三种 JAX-RS 实现(Apache CXF、Jersey 和 JBoss RestEasy),以及三种不同的 servlet 容器(Apache Tomcat、JBoss Undertow 和 Eclipse Jetty)。除此之外,Hammock 还...

库穆鲁兹(KumuluzEE)

Sunesis 是开源 KumuluzEE 项目的赞助商,该项目实现了 Eclipse MicroProfile 规范。KumuluzEE 定义了自己作为一个使用 Java 和 Java EE 技术的轻量级微服务框架,并且是 Eclipse MicroProfile 兼容的实现。KumuluzEE 允许你使用仅需要的组件来引导一个 Java EE 应用程序,并且还支持将微服务打包和作为 uberjars 运行。与其他支持 uberjars 的实现一样,你可以通过输入以下命令来运行你的微服务:

$ java -jar <executable JAR file>

KumuluzEE 还提供了一个 POM 生成器,它可以创建一个带有所选选项和功能的 pom.xml,用于你计划开发的微服务。POM 生成器提供了由 KumuluzEE 支持的可选的清晰和组织的列表,包括在 pom.xml 文件中。

KumuluzEE 为不同的 MicroProfile API 提供了一些示例。

有关 KumuluzEE 实现 Eclipse MicroProfile 的文档,请参考 ee.kumuluz.com/microprofile

最后,KumuluzEE 提供了一些有趣的教程在 ee.kumuluz.com/tutorials/

启动器

Fujitsu 是开源 Launcher 项目的赞助商,该项目实现了 Eclipse MicroProfile 规范。Launcher 利用了内嵌的 GlassFish 服务器和 Apache Geronimo MicroProfile API 实现。你可以将你的微服务作为 WAR 文件运行,如下所示:

$ java -jar launcher-1.0.jar --deploy my-app.war

此外,Launcher 可以创建 uberjars。要创建并运行你的微服务作为 uberjar,首先生成 uberjar,然后使用 java -jar 调用它,如下所示:

$ java -jar launcher-1.0.jar --deploy my-app.war --generate my-uber.jar$ java -jar my-uber.jar

有关 Launcher 的文档非常稀少且有限。你可以找到有关 Launcher 的使用信息在 github.com/fujitsu/launcher/blob/master/doc/Usage.adoc ...

Helidon

Oracle Corporation 是开源 Helidon 项目的赞助商,该项目实现了 Eclipse MicroProfile 规范。Helidon 是一组 Java 库,可让开发者编写微服务。它利用了 Netty,一个非阻塞的 I/O 客户端服务器框架。Helidon 是一个应用程序组装器,因为它可以生成应用程序 JAR。一旦你构建了应用程序 JAR,你可以使用以下命令执行它:

$ java -jar <executable JAR file>

Helidon 有两种版本:SE 和 MP。Helidon SE 是由所有 Helidon 库提供的功能编程风格,它提供了一个名为 MicroFramework 的微服务框架。Helidon MP 实现了微服务的 MicroProfile 规范,并建立在 Helidon 库之上。没有样本项目生成工具,但 Helidon 提供了一组丰富且详尽的文档手册。

Helidon 的文档可以在 helidon.io/docs/latest/#/about/01_overview 找到。

Helidon SE 提供了一个 WebServer,这是一个用于创建 Web 应用程序的异步和反应式 API。Helidon MP 提供了一个封装 Helidon WebServer 的 MicroProfile 服务器实现。

为当前实现生成示例代码

如前几节所述,大多数 MicroProfile 实现并没有提供自己的示例项目生成器。相反,它们只提供文档。这时 MicroProfile Starter 就派上用场了!

MicroProfile Starter 由 MicroProfile 社区赞助,是一个为所有通过 MicroProfile TCK 的 MicroProfile 规范生成示例项目和源代码的工具。在第二章治理和贡献中,我们为您提供了 MicroProfile Starter 的概览。为了避免重复,我们只想指出您可以在下拉菜单中选择 MicroProfile 版本如下:...

其他实现 MicroProfile 的项目

小型 Rye 是一个开源项目,它开发了任何供应商或项目都可以使用的 Eclipse MicroProfile 实现。这是一个社区努力,每个人都可以参与和贡献给小型 Rye,smallrye.io。作为一个例子,社区最近将微服务扩展项目贡献给了小型 Rye,从而使其通过配置源、OpenAPI、健康、JAX-RS 和 REST 客户端扩展丰富了其功能。

微服务扩展项目网站是www.microprofile-ext.org,其 GitHub 是github.com/microprofile-extensions

小型 Rye 实现已经通过了 Eclipse MicroProfile TCKs 的测试。

消费小型 Rye 的开源项目有 Thorntail(thorntail.io)、WildFly(wildfly.org)和 Quarkus(quarkus.io)。

Quarkus

开源的 Quarkus 项目于 2019 年首次亮相。Quarkus 是一个可以编译成原生机器语言或构建到 HotSpot(OpenJDK)的 Kubernetes 本地 Java 栈。使用 Quarkus 时,您的应用程序消耗非常少的内存,具有出色的性能,可以处理高调用吞吐量,并且启动时间非常快(即引导加上首次响应时间),使 Quarkus 成为容器、云本地和无服务器部署的绝佳运行时。Quarkus 还提供了一个扩展框架,允许将库和项目quarking(注:此处应为“转化为 Quarkus 兼容的形式”),使它们与 Quarkus 无缝协作。

Quarkus 的使命是将您的整个应用程序及其使用的库转换为最优...

如何将生成的 MicroProfile 项目quarking

在我们开始讲解如何使用 MicroProfile Starterquark生成 MicroProfile 项目的步骤之前,我们首先需要确保已经在您的环境中安装、定义和配置了 GRAALVM_HOME。为此,请按照以下步骤操作:

  1. 访问https://github.com/oracle/graal/releases,并根据您的操作系统下载 GraalVM 的最新版本。

  2. 将下载的文件解压缩到您选择的子目录中。顺便说一下,解压缩将创建一个 GraalVM 子目录,例如:

$ cd $HOME
$ tar -xzf graalvm-ce-1.0.0-rc16-macos-amd64.tar.gz
  1. 打开一个终端窗口,创建一个名为GRAALVM_HOME的环境变量,例如:
$ export GRAALVM_HOME=/Users/[YOUR HOME DIRECTORY]/graalvm-ce-1.0.0-rc13/Contents/Home

既然我们已经安装了 GraalVM,我们可以继续讲解如何使用 MicroProfile Starterquark生成 MicroProfile 项目的步骤:

  1. 首先,将您的浏览器指向start.microprofile.io并选择 Thorntail 作为 MicroProfile 服务器。

您可以利用以下步骤将任何现有的 Java 应用程序quark化。

如果您不记得如何进行此操作,请转到第二章,治理和贡献,并遵循MicroProfile Starter 快速入门部分中的说明,直到第 5 步,其中demo.zip文件下载到您的本地Downloads目录。

  1. 使用您喜欢的解压缩工具展开demo.zip文件。如果您没有自动展开demo.zip文件,请使用以下命令(假设是 Linux;对于 Windows,请使用等效命令):
$ cd $HOME/Downloads
$ unzip demo.zip

这将创建一个名为demo的子目录,在其下有一个完整的目录树结构,包含所有使用 Maven 构建和运行 Thorntail 示例 MicroProfile 项目的源文件。

  1. 与其在demo子目录中进行更改,不如让我们创建一个名为Qproj4MP的第二个目录,与demo子目录并列,如下所示:
$ mkdir $HOME/Downloads/Qproj4MP

这将在您Downloads目录中现有demo子目录的同级创建一个名为Qproj4MP的子目录。

  1. 将您的目录更改到Qproj4MP,并通过输入以下命令创建一个空的 Quarkus 项目:
$ cd $HOME/Downloads/Qproj4MP
$ mvn io.quarkus:quarkus-maven-plugin:0.12.0:create \
 -DprojectGroupId=com.example \
 -DprojectArtifactId=demo \
 -Dextensions="smallrye-health, smallrye-metrics, smallrye-openapi, smallrye-fault-tolerance, smallrye-jwt, resteasy, resteasy-jsonb, arc"
  1. Qproj4MP目录中,删除src子目录并用以下命令替换为 Thorntail 示例 MicroProfile 项目的src子目录:
$ cd $HOME/Downloads/Qproj4MP  # ensuring you are in the Qproj4MP sub-directory
$ rm -rf ./src
$ cp -pR $HOME/Downloads/demo/src .
  1. Quarkus 和 Thorntail 对某些配置和 web 应用程序相关文件的位置有不同的期望。因此,为了使 Quarkus 满意,让我们通过输入以下命令来复制一些文件:
$ cd $HOME/Downloads/Qproj4MP # ensuring you are in the Qproj4MP sub-directory
$ mkdir src/main/resources/META-INF/resources
$ cp /Users/csaavedr/Downloads/demo/src/main/webapp/index.html src/main/resources/META-INF/resources
$ cp -p src/main/resources/META-INF/microprofile-config.properties src/main/resources/application.properties

我们本可以将这些文件从它们原来的位置移动,但我们选择在这个示例中只是复制它们。

  1. MicroProfile Starter 生成的 Thorntail 示例 MicroProfile 项目,其src子目录的内容你已经复制到了Qproj4MP,使用了一个名为bouncycastle的安全库。这是因为生成的代码包含了一个 MicroProfile JWT Propagation 规范的示例,该规范允许你在微服务之间传播安全性。因此,我们还需要在 Quarkus 项目的 POM 文件中再添加两个依赖,一个是bouncycastle,另一个是nimbusds

下一个 sprint 版本的 MicroProfile Starter 将不再包含 Thorntail 服务器代码生成中的bouncycastle依赖。

为了添加这些依赖项,请编辑你$HOME/Downloads/Qproj4MP目录下的pom.xml文件,并在<dependencies>部分输入以下代码块:

 <dependency>
 <groupId>org.bouncycastle</groupId>
 <artifactId>bcpkix-jdk15on</artifactId>
 <version>1.53</version>
 <scope>test</scope>
 </dependency>
 <dependency>
 <groupId>com.nimbusds</groupId>
 <artifactId>nimbus-jose-jwt</artifactId>
 <version>6.7</version>
 <scope>test</scope>
 </dependency>

现在我们准备编译 quarked 的 MicroProfile 项目。

  1. 除了支持构建可以在 OpenJDK 上运行的 Java 项目外,Quarkus 还支持将 Java 项目编译到底层机器码。输入以下命令来编译 quarked 示例项目到原生代码:
$ cd $HOME/Downloads/Qproj4MP # ensuring you are in the Qproj4MP sub-directory
$ ./mvnw package -Pnative
  1. 要运行应用程序,请输入以下命令:
$./target/demo-1.0-SNAPSHOT-runner

要测试应用程序,请遵循Quick tour of MicroProfile Starter章节中治理和贡献部分的说明,从第 10 步开始。

  1. 如果你想要在开发模式下运行 quarked 项目,首先停止正在运行的进程,然后输入以下命令:
$ cd $HOME/Downloads/Qproj4MP # ensuring you are in the Qproj4MP sub-directory
$ ./mvnw compile quarkus:dev

在此阶段,你可以选择一个 IDE,比如 Visual Studio Code 或 Eclipse IDE,来打开项目,并开始修改源代码。Quarkus 支持热重载,这意味着,只要你对源代码做了任何更改,Quarkus 会在后台重新构建并重新部署你的应用程序,这样你就可以立即看到并测试更改的效果。此外,如果你在源代码中犯了语法错误,Quarkus 会将有意义的错误信息传播到网页应用程序中,帮助你修复错误,提高你的工作效率。

  1. 如果你想要生成一个可执行的应用程序 JAR,请输入以下命令:
$ cd $HOME/Downloads/Qproj4MP # ensuring you are in the Qproj4MP sub-directory
$ ./mvn clean package
  1. 要运行可执行的应用程序 JAR,请输入以下命令:
$ java -jar target/demo-1.0-SNAPSHOT-runner.jar

创建一个与应用程序 JAR 并列的 lib 目录,其中包含运行所需的所有库文件。

我们向您展示了使用 MicroProfile Starter 生成的 MicroProfile 项目的quark步骤。尽管这些步骤适用于特定的生成项目,但您可以使用相同的说明来quark一个现有的 Java 应用程序或微服务,以便您可以利用 Quarkus 提供的好处,如低内存消耗、快速的启动时间以及对 Java 代码的原生编译,以便您可以在容器、云和函数即服务环境中高效运行。无论您使用 MicroProfile 的哪个实现,MicroProfile 为最终用户提供的很大好处就是互操作性。这意味着您可以设计一个使用不同 MicroProfile 实现的微服务应用程序,这是下一节的主题。

MicroProfile 互操作性——会议应用程序

会议应用程序,首次介绍(www.youtube.com/watch?v=iG-XvoIfKtg)于 2016 年 11 月在比利时 Devoxx 上,是一个展示不同 MicroProfile 供应商实现集成和互操作性的 MicroProfile 演示。这很重要,因为它展示了规范的实现和接口之间的分离,提供了一个允许供应商开发并提供自己的实现的平台,这些实现可以与其他竞争性实现共存。所有实现中的通用接口还为最终用户提供了使用任何 MicroProfile 实现...的好处。

总结

在本章中,我们了解了市场上现有的开源 MicroProfile 实现,它们是什么类型的实现,如何获取关于它们的更多信息,以及如何使用 MicroProfile Starter 为这些实现生成示例代码。我们还介绍了最新的 MicroProfile 实现参与者 Quarkus,它显著提高了 Java 在解释和编译模式下的启动时间和内存消耗,进一步优化了适用于云原生微服务和无服务器环境的 MicroProfile。您还了解了 The Conference Application,它展示了 MicroProfile 在不同实现之间的互操作性。

作为 Eclipse MicroProfile 的消费者,其跨实现互操作的特性,您有自由选择对您的组织最有意义或最适合您环境的实现,最终给您提供选择正确工具的正确任务的选项。此外,您不必局限于单一供应商的商业支持版 Eclipse MicroProfile,因此,您可以根据自己的条件进行谈判,并从不同供应商提供的丰富的 MicroProfile 特性中进行选择。

在下一章,我们将涵盖整个 MicroProfile API 集的全代码示例。

问题

  1. 目前市场上存在多少种 MicroProfile 实现?请列出它们。

  2. 应用服务器与应用组装器之间有什么区别?

  3. 描述市场上存在的八种 MicroProfile 实现。

  4. 什么是 Quarkus?

  5. 编译时启动是什么?

  6. Quarkus 适用于哪种类型的部署?

  7. 什么是 Quarkus 扩展框架?

  8. 会议应用程序展示了什么关键优势?

第四部分:一个工作的 MicroProfile 示例

本部分将介绍一个展示 MicroProfile 的应用程序。

本部分包含以下章节:

  • 第八章,一个工作的 Eclipse MicroProfile 代码示例

第九章:一个工作的 Eclipse MicroProfile 代码示例

在本章中,我们将讨论一个使用本书前面介绍的各种 MicroProfile 功能的示例应用程序。在本章中,我们将使用的 MicroProfile 运行时是 Quarkus 运行时,这是一个为 GraalVM 和 OpenJDK HotSpot 量身定制的 Kubernetes 原生 Java 堆栈,由最佳的 Java 库和标准组成。我们将要涵盖的关键主题包括以下内容:

  • 应用程序和 MicroProfile 容器行为的使用配置

  • 现实的健康检查

  • 使用外部 JWT 安全提供程序保护应用程序

  • 使用 Jaeger 实例集成并查看跟踪信息

  • 使用 Swagger 检查微服务端点信息

  • 查看个体...

技术要求

为本章,我们需要以下内容:

  • 一个集成开发环境(IDE)

  • JDK 1.8+安装并配置了JAVA_HOME

  • Apache Maven 3.5.3+

  • 一个运行中的 Docker 环境

本章的代码可以在github.com/PacktPublishing/Hands-On-Enterprise-Java-Microservices-with-Eclipse-MicroProfile/tree/master/Chapter08-mpcodesample找到。

本章中的示例可以通过 GraalVM(github.com/oracle/graal/releases/tag/vm-1.0.0-rc16)与 Quarkus 的集成编译成原生二进制。这需要安装 1.0-RC16 版本的 Graal VM 和一个运行中的 C 开发环境,以及一个工作环境。关于生成原生镜像的详细要求,可以在quarkus.io/guides/building-native-image-guide找到。

多服务 MicroProfile 应用程序的示例架构

本章我们将要介绍的示例应用程序由一个 HTML 前端、两个基于 MicroProfile 的微服务、两个我们使用 Docker 启动的外部服务以及一个我们无法控制的网络上的外部时间服务组成。我们示例应用程序的架构如下所示:

此图中的关键元素包括以下内容:

  • Svcs1 Image:这是一个使用 Quarkus 运行时的 REST 端点的集合,其中包括了 MP-HC、MP-Metrics、MP-OT、MP-JWT、MP-OAPI、MP-FT 和 MP-RC。

  • Svcs2 Image:这是一组使用...的 REST 端点集合

运行示例应用程序

示例应用程序设计为从四个不同的 shell 运行,让我们将它们组织成四个 shell 窗口,如图所示:

Docker Shell Web Shell
Svcs1 Shell Svcs2 Shell

在每一个 shell 中,运行以下相应命名的部分中概述的命令。

Docker shell 命令

提供预配置的服务器/服务的一种常见方法是使用包含服务和所有依赖项的 Docker 镜像。在这个例子中,我们使用 Docker 来运行 KeyCloak 和 Jaeger 镜像。如果你不熟悉 Docker 或者没有安装docker命令,请参阅安装 Docker 的说明,针对你的平台docs.docker.com/v17.12/install/

本项目依赖于 KeyCloak 生成 MP-JWT 令牌。要启动 KeyCloak Docker 容器,请在你的壳中运行以下命令:

docker run -d --name keycloak -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -p 8180:8180 -v `pwd`/packt-mp-realm.json:/config/quarkus-packt.json -it jboss/keycloak:6.0.1 -b 0.0.0.0 ...

服务 1 壳命令

接下来,在你之前被要求打开的 Svcs1 终端窗口中,导航到项目的svcs1子目录,然后运行以下命令以在开发模式下执行svcs1镜像:

mvn compile quarkus:dev

你将看到以下输出:

Scotts-iMacPro:svcs1 starksm$ mvn compile quarkus:dev
[INFO] Scanning for projects...
...
20:56:27 INFO [io.quarkus]] (main) Quarkus 0.15.0 started in 2.492s. Listening on: http://[::]:8081
20:56:27 INFO [io.quarkus]] (main) Installed features: [cdi, jaeger, resteasy, resteasy-jsonb, security, smallrye-fault-tolerance, smallrye-health, smallrye-jwt, smallrye-metrics, smallrye-openapi, smallrye-opentracing, smallrye-rest-client, swagger-ui]

在输出中,我们看到这个实例正在监听8081端口的 HTTP 请求,并且我们看到了各种支持我们微服务特征使用的 Quarkus 特性。

服务 2 壳命令

接下来,在你之前被要求打开的 Svcs2 终端窗口中,切换到项目的svcs2子目录,然后运行以下命令以构建svcs2镜像:

mvn clean package

构建完成后,要运行svcs2 JAR,请输入以下命令:

 java -jar target/sample-svcs2-runner.jar

你会得到以下输出:

Scotts-iMacPro:svcs2 starksm$ java -jar target/sample-svcs2-runner.jar...20:58:55 INFO [io.quarkus]] (main) Quarkus 0.15.0 started in 0.936s. Listening on: http://[::]:808220:58:55 INFO [io.quarkus]] (main) Installed features: [cdi, jaeger, resteasy, resteasy-jsonb, security, smallrye-health, smallrye-jwt, smallrye-metrics, smallrye-opentracing, smallrye-rest-client]

在这里,我们...

网页壳命令

接着,在你之前被要求打开的 Web 壳终端窗口中,克隆这个项目到你的电脑,切换到web子目录,然后运行以下命令以在开发模式下执行 Web 应用程序:

mvn clean package

构建完成后,要运行 Web 子项目 JAR,请输入以下命令:

java -jar target/sample-web-runner.jar

一旦应用程序启动运行,将你的浏览器指向localhost:8080/index.html的 Web 应用程序。在下一节中,我们将回顾 Web 应用程序的详细信息。

示例应用程序的详细信息

让我们详细讨论我们应用程序中的各种标签页。

配置标签页

应用程序的初始视图显示了配置标签页,如下面的屏幕截图所示:

页面上的三个链接指的是Chapter08-mpcodesample/svcs1子项目中的io.packt.sample.config.ConfigTestController类中的参考端点。点击各个链接会显示 MP-Config 值。前一个屏幕截图中显示的值对应于第二个链接和injected.piValue配置值。以下是Chapter08-mpcodesample/svcs1/src/main/resources/application.properties中的相关设置:

# MP Config values for ConfigTestController
injected.value=Injected value
injected.piValue=3.1415926532
lookup.value=A Lookup value

此处值得注意的是,通过在ConfigTestController中的@ConfigProperty(name = "injected.piValue", defaultValue = "pi5=3.14159")注解中覆盖了默认的五位数字值,设置为前一个屏幕截图中显示的完整 10 位π值。

健康标签页

点击应用的“健康”标签页会显示如下页面:

页面上的链接对应于svcs1svcs2镜像的health检查端点。选择任何一个都会显示该镜像的health检查输出。svcs1镜像的health检查由io.packt.sample.health.ServiceHealthCheckio.packt.sample.health.CheckDiskspace组成。此外,ServiceHealthCheck只是一个总是返回运行状态的模拟实现。CheckDiskspace健康检查程序查看使用 MP-Config health.pathToMonitor属性设置的路径,然后根据...设置程序状态为运行/停止。

“指标”选项卡

“指标”选项卡显示以下包含三个链接的视图:

第一个链接访问io.packt.sample.metric.MetricController类中的以下端点:

@Path("timed")
@Timed(name = "timed-request")
@GET
@Produces(MediaType.TEXT_PLAIN)
public String timedRequest() {
    long start = System.currentTimeMillis();
    // Demo, not production style
    int wait = new Random().nextInt(1000);
    try {
        Thread.sleep(wait);
    } catch (InterruptedException e) {
        // Demo
        e.printStackTrace();
    }
    long end = System.currentTimeMillis();
    long delay = end - start;

    doIncrement();
    long count = getCustomerCount();
    return String.format("MetricController#timedRequest, delay[0-1000]=%d, 
    count=%d", delay, count);
}

这使用@Timed(name = "timed-request")注解注解了timed路径端点。该方法使用 0-1000 毫秒之间的随机延迟生成响应时间分布。下一个链接是直接链接到timedRequest()方法的应用程序级指标。MP-Metrics 规范将路径定义为metrics/application/io.packt.sample.metric.MetricController.timed-request。在访问第一个链接生成一系列响应时间之后,通过访问第二个链接获取timedRequest()方法指标将显示如下:

# TYPE application:io_packt_sample_metric_metric_controller_timed_request_rate_per_second gauge
application:io_packt_sample_metric_metric_controller_timed_request_rate_per_second 0.4434851530761856
# TYPE application:io_packt_sample_metric_metric_controller_timed_request_one_min_rate_per_second gauge
application:io_packt_sample_metric_metric_controller_timed_request_one_min_rate_per_second 0.552026648777594
...
# TYPE application:io_packt_sample_metric_metric_controller_timed_request_seconds summary
application:io_packt_sample_metric_metric_controller_timed_request_seconds_count 6.0
application:io_packt_sample_metric_metric_controller_timed_request_seconds{quantile="0.5"} 0.923901552
...
application:io_packt_sample_metric_metric_controller_timed_request_seconds{quantile="0.999"} 0.970502841

这是@Timed样式指标生成的信息范围。最后一个链接访问返回镜像中所有可用指标的metrics端点。

“OpenTracing”选项卡

“OpenTracing”选项卡显示以下带有两个链接的视图:

第一个链接访问以下io.packt.sample.tracing.TracedEndpoint方法:

@GET@Path("/randomDelay")@Produces(MediaType.TEXT_PLAIN)@Traced(operationName = "TracedEndpoint#demoRandomDelay")public String randomDelay() {    long start = System.currentTimeMillis();    // 0-5 seconds random sleep    long sleep = Math.round(Math.random() * 5000);    try {        Thread.sleep(sleep);    } catch (InterruptedException e) {        e.printStackTrace();    }    long end = System.currentTimeMillis();    return String.format("TracedEndpoint.randomDelay[0-5000], elapsed=%d",     (end - start));}

方法...

“OpenAPI”选项卡

“OpenAPI”选项卡视图包含两个链接,如下屏幕截图所示:

第一个链接生成一个 OpenAPI 文档,一个包含应用程序中所有端点描述的 YAML 文件。这可以输入到其他能够消费 OpenAPI 格式的程序或应用程序中。第二个链接是此类应用程序的一个示例,即 Swagger UI。打开该链接将打开一个类似于以下的新窗口:

此示例应用视图包含三个部分。第一部分是在 JAX-RS 应用 bean 上通过 OpenAPI 注解指定的信息,如下代码片段所示:

@ApplicationPath("/demo1")
@LoginConfig(authMethod = "MP-JWT", realmName = "quarkus-quickstart")
@OpenAPIDefinition(
    info = @Info(
        title = "Quarkus MicroProfile 2.2 Extensions Demo",
        version = "1.0",
        contact = @Contact(
            name = "QUARKUS - COMMUNITY",
            url = "https://quarkus.io/community/",
            email = "quarkus-dev+subscribe@googlegroups.com"),
        license = @License(
            name = "Apache 2.0",
            url = "http://www.apache.org/licenses/LICENSE-2.0.html")
    ),
    servers = {
        @Server(url = "http://localhost:8080/", description = "demo1 host"),
        @Server(url = "http://localhost:8081/", description = "demo2 host")
    },
    externalDocs = @ExternalDocumentation(url="http://microprofile.io", description = 
    "Eclipse MicroProfile Homepage")
)
public class DemoRestApplication extends Application {
...

将此信息与 Swagger UI 中显示的信息进行比较,可以看出所有@OpenAPIDefinition注解的信息都已经被整合到了 UI 顶部。Swagger UI 的下一个部分,带有timedefault子标题的部分对应于从应用程序 REST 端点获取的操作信息。default部分对应于没有包含任何 OpenAPI 规范注解的端点。对于应用程序中发现的任何 JAX-RS 端点,都会有一个默认行为来创建一个 OpenAPI 端点定义。

time部分对应于以下io.packt.sample.restclient.TimeService端点代码片段,该片段包含了@Tag@ExternalDocumentation@Operation MP-OpenAPI 注解:

@GET
@Path("/now")
@Produces(MediaType.APPLICATION_JSON)
@Tag(name = "time", description = "time service methods")
@ExternalDocumentation(description = "Basic World Clock API Home.",
    url = "http://worldclockapi.com/")
@Operation(summary = "Queries the WorldClockApi using the MP-RestClient",
    description = "Uses the WorldClockApi type proxy injected by the 
    MP-RestClient to access the worldclockapi.com service")
public Now utc() {
    return clockApi.utc();
}

如果您展开时间部分下的第一个操作,您将获得一个这样的视图:

您可以看到@Tag已经定义了时间部分及其描述,而@Operation注解已经增强了操作摘要和描述部分。这显示了您可以如何使用 MP-OAPI 注解和像 Swagger UI 这样的 OpenAPI 感知应用程序为您的端点的消费者提供更多信息。

密钥保管库标签

接下来我们跳到密钥保管库标签,因为 RestClient 和 JWT 标签包括需要 JWT 才能访问端点的受保护调用。当你第一次访问密钥保管库标签时,它应该看起来像这样:

它不会显示任何令牌信息,状态行应在刷新复选框下方指示(未认证)。点击绿色的登录按钮,将出现以下登录屏幕:

分别输入以下内容作为用户名和密码字段:

  • packt-mp-book

  • password

这...

JWT 标签

在点击 JWT 标签后,您应该看到一个类似于以下内容的视图,其中有两个端点链接:

第一个链接向一个未受保护的端点发起请求,如果存在的话,它将打印 JWT 中的名称以及upn声明。

然而,由于 Web 前端没有为这个请求提供 JWT,输出部分将显示以下内容:

Hello[open] user=anonymous, upn=no-upn

点击第二个链接可以访问该端点的受保护版本,其中包含此代码片段:

public class JwtEndpoint {
    @Inject
    private JsonWebToken jwt;
    @Inject
    @Claim(standard = Claims.raw_token)
    private ClaimValue<String> jwtString;
    @Inject
    @Claim(standard = Claims.upn)
    private ClaimValue<String> upn;
    @Context
    private SecurityContext context;
...
    @GET
    @Path("/secureHello")
    @Produces(MediaType.TEXT_PLAIN)
    @RolesAllowed("user") // 1
    public String secureHello() {
        String user = jwt == null ? "anonymous" : jwt.getName(); // 2
        String scheme = context.getAuthenticationScheme(); // 3
        boolean isUserInRole = context.isUserInRole("PacktMPUser"); // 4
        return String.format("Hello[secure] user=%s, upn=%s, scheme=%s, 
        isUserInRole(PacktMPUser)=%s", user, upn.getValue(), 
        scheme, isUserInRole);
    }

让我们讨论重要的行:

  1. @RolesAllowed("user")注解表明该端点是受保护的,调用者需要user角色。我们之前看到的 JWTgroups声明就是这个角色。

  2. 用户通过getName()方法从 JWT 获取。如 MP-JWT 章节中所解释,这映射到 JWT 中的upn声明。

  3. 当前的安全认证方案是从注入的SecurityContext中获取的。

  4. 通过检查调用者是否具有PacktMPUser角色来进行程序安全检查。由于我们之前看到的 JWT 群组声明具有此角色,因此检查将返回真。

这些信息被组合成一个字符串,它是secureHello方法的返回值。点击 demo1/jwt/secureHello 链接按钮,在响应区域产生以下输出字符串:

Hello[secure] user=packt-mp-book, upn=packt-mp-book, scheme=MP-JWT, isUserInRole(PacktMPUser)=true

通过使用@RolesAllowed注解和与 MP-JWT 功能的集成,我们可以看到我们如何既能保护微服务端点的安全,又能根据认证 JWT 中的内容引入应用程序行为。接下来,让我们回到 RestClient 标签页。

RestClient 标签页

RestClient 标签页包含三个链接,如图所示:

这些链接对应于使用外部世界时钟公共端点来返回有关当前时间的信息的端点。以下 MP-RC 接口已创建以封装外部端点:

@RegisterRestClient(baseUri = WorldClockApi.BASE_URL)public interface WorldClockApi {    static final String BASE_URL = "http://worldclockapi.com/api/json";    @GET    @Path("/utc/now")    @Produces(MediaType.APPLICATION_JSON)    Now utc();    @GET    @Path("{tz}/now")    @Produces(MediaType.APPLICATION_JSON)    Now tz(@PathParam("tz") String tz);}

总结

本章带我们了解了由一个 web 应用程序、两个使用 MP 功能的微服务镜像组成的新 Quarkus 实现、一个外部 web 服务以及两个基于 Docker 的服务组成的示例服务网格。这展示了各种 MP 功能与外部服务在云环境中的交互,以及与本地网格环境外部的 web 服务的集成。这应该让您了解到使用 MicroProfile API 和实现编写微服务的步骤。

在下一章中,我们将查看正在开发中的 MicroProfile 规范,以了解正在探索的方向。虽然这些规范目前不在 MicroProfile 核心功能集之外,但它们是未来可能包含在内的候选项,并且查看它们将让我们了解 MicroProfile 可能的发展方向。

问题

  1. MP-Config 设置是否影响应用程序代码、MP 功能代码,还是两者都会影响?

  2. 您能够更新health.pathToMonitor到一个有效的路径并看到一个更新的健康状态回复吗?

  3. 健康标签页上的Svcs2健康状态链接(http://localhost:8082/health)显示的是什么输出?如果您停止 KeyCloak Docker 镜像并再次点击链接,输出会发生变化吗?

  4. 如果您在没有至少先点击Timed端点链接(http://localhost:8080/demo1/metric/timed)的情况下选择 Metrics 标签页中的MetricController.timed-request链接(http://localhost:8080/metrics/application/io.packt.sample.metric.MetricController.timed-request),会发生什么?

  5. 转到 RestClient 标签页并点击链接,确保您有一个有效的...

进一步阅读

审查代码、尝试修改,然后与更新后的代码互动,这是了解示例服务背后更多细节的好方法。Quarkus MicroProfile 实现支持实时重新加载功能,允许您在不重新构建的情况下进行更改。关于这一主题的更多信息,请参阅 Quarkus 网站上的 Maven 工具文档(quarkus.io/guides/maven-tooling.html)。

第五部分:未来展望

本节介绍了不属于当前范畴的一些现有项目,例如候选 API,并讨论了 MicroProfile 如何适应多云环境。

本节包含以下章节:

  • 第九章,响应式编程与未来发展

  • 第十章,MicroProfile 在多云环境中的应用

第十章:反应式编程与未来发展

事件驱动架构已经存在很长时间,异步方法调用、消息驱动 bean、事件控制逻辑等是开发者熟悉的构造。然而,随着云资源和按需可扩展性的普及和采用,组织对可以利用无服务器和函数即服务类型环境的反应式编程方法重新产生了兴趣。Eclipse MicroProfile 还包括目前不在 Eclipse MicroProfile 伞/平台发布中的反应式编程相关项目规范。

除了这些,Eclipse MicroProfile 沙盒中还有社区目前正在讨论、实施和评估的项目,以决定它们是否应升级为正式的 MicroProfile 项目。本章将帮助您了解与反应式编程相关的当前 MicroProfile 规范,并为您提供一些已经在进行中和即将到来的项目,这些项目位于伞下/平台发布之外以及 MicroProfile 沙盒中。本章将涵盖以下主题:

  • 反应式消息传递的概述

  • 解释 Eclipse MicroProfile 内的反应式消息传递架构

  • 描述与反应式编程相关的 Eclipse MicroProfile 规范

  • 使用 Eclipse MicroProfile 反应式消息规范的示例

  • 概述不在 Eclipse MicroProfile 伞下或平台发布中的 MicroProfile 项目/规范

  • 描述位于 Eclipse MicroProfile 沙盒中的项目

  • 深入了解 Eclipse MicroProfile 与 Jakarta EE 当前的关系以及它们可能的未来分析

Eclipse MicroProfile 中的响应式编程工作

在撰写本文时,属于 Eclipse MicroProfile 的反应式相关规范包括 Reactive Streams Operators、Reactive Messaging 和 Context Propagation。Eclipse MicroProfile 社区内的反应式工作仍在不断发展,未来可能会有新的规范出现,同时现有反应式相关规范也会有新的版本发布。

反应式消息传递概述

《反应式宣言](https://www.reactivemanifesto.org/)定义了反应式系统的特性,包括一个用于构建弹性、恢复性系统的异步消息核心。这通常通过以下图表进行说明:

想法是通过异步消息交互促进弹性、弹性,进而提高响应性。

MicroProfile 反应式消息MP-RM)规范旨在通过事件驱动的微服务使基于微服务的应用程序具有反应式系统的特性。该规范关注多样性,适用于构建不同类型的架构和应用程序。

可以使用反应式消息实现与不同服务和资源的不对称交互。通常,异步数据库驱动可以与反应式消息一起使用,以非阻塞和非同步的方式读写数据存储。

在构建微服务时,命令查询责任分离CQRS)和事件源模式为微服务之间的数据共享提供了答案(martinfowler.com/bliki/CQRS.html)。反应式消息也可以作为 CQRS 和事件源机制的基础,因为这些模式拥抱消息传递作为核心通信模式。

MicroProfile 反应式消息架构

使用反应式消息的应用程序由消费、生产和处理消息的 CDI Bean 组成。这些消息可以是应用程序内部的,也可以是通过外部消息代理发送和接收的,如下面的图表所示:

这个图表展示了一个 Kafka 主题向第一个 Bean 发布消息,该 Bean 进行一些处理后又将其发布给第二个 Bean,第二个 Bean 进行自己的处理/过滤,最后将消息作为 AMQP 主题发布。

正如我们在查看 MP-RM 示例时将看到的,应用 Bean 包含用 @Incoming 和/或 @Outgoing ... 注解的方法。

消息形状

MP-RM 规范定义了许多受支持的签名类型,Bean 可以使用它们来定义发布和订阅行为。这些签名依赖于以下列表中概述的几个关键类型:

  • org.reactivestreams.Publisher:一个 Reactive Streams Publisher<T> 是提供潜在无限数量序列元素的提供者,根据从其链接订阅者接收的需求发布它们。

  • org.reactivestreams.Subscriber:一个用于向 Publisher 信号需求的 Reactive Stream Subscriber<T> 接口。它提供了订阅信息事件、零个或多个数据事件以及错误和完成事件。

  • org.reactivestreams.Processor:这个 Reactive Stream Processor<T,R> 接口简单地扩展了 Subscriber<T>Publisher<R> 两个接口。

  • org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder:MP Reactive Streams 操作符 PublisherBuilder 接口允许你从各种源构建 Reactive Streams Publisher 并应用操作来转换/过滤最终发布的消息。

  • org.eclipse.microprofile.reactive.streams.operators.ProcessorBuilder:微 Profile 反应流操作符ProcessorBuilder接口允许你从各种来源构建反应流Processor并应用操作来转换/过滤最终发布的消息。

  • org.eclipse.microprofile.reactive.streams.operators.SubscriberBuilder:微 Profile 反应流操作符SubscriberBuilder接口允许你从各种来源构建反应流Subscriber并应用操作来转换/过滤最终发布的消息。

  • java.util.concurrent.CompletionStage:这个 JDK 并发util包接口定义了一个通常是异步的计算阶段,并计算一个动作或值。CompletionStage可以组合使用,以便执行阶段图以产生最终结果。

  • org.eclipse.microprofile.reactive.messaging.Message<T>:一个提供类型为T的载荷包装器并有一个ack方法来确认收到消息的 MP-RM 接口。

定义了这些类型后,我们可以查看 MP-RM 支持的各种方法,这些方法通过将消息推送到出站通道来产生数据。所有发布者方法类型都有@Outgoing("channel-name")注解,并支持如下签名:

  • Publisher<Message<T>> 方法()

  • Publisher<T> 方法()

  • PublisherBuilder<Message<T>> 方法()

  • PublisherBuilder<T> 方法()

  • T 方法()

  • CompletionStage<T> 方法()

消费者方法都有一个@Incoming("channel-name")注解,并支持如下签名:

  • Subscriber<Message<T>> 方法()

  • Subscriber<T> 方法()

  • SubscriberBuilder<Message<T>>

  • SubscriberBuilder<T>

  • void 方法(Message<T> 载荷)

  • void 方法(T 载荷)

  • CompletionStage<?> 方法(Message<T> 载荷)

  • CompletionStage<?> 方法(T 载荷)

既消耗数据又产生数据的方法被称为处理器,并将具有@Incoming("channel-in")@Outgoing("channel-out")注解。支持的签名如下:

  • Processor<Message<I>, Message<O>> 方法()

  • Processor<I, O> 方法();

  • ProcessorBuilder<Message<I>, Message<O>>方法()

  • ProcessorBuilder<I, O> 方法();

  • Publisher<Message<O>> 方法(Message<I> msg)

  • Publisher<O> 方法(I 载荷)

  • PublisherBuilder<Message<O>> 方法(Message<I> msg)

  • PublisherBuilder<O> 方法(I 载荷)

  • Message<O> 方法(Message<I> msg)

  • O 方法(I 载荷)

  • CompletionStage<Message<O>> 方法(Message<I> msg)

  • CompletionStage<O> 方法(I 载荷)

  • Publisher<Message<O>> 方法(Publisher<Message<I>> pub)

  • PublisherBuilder<Message<O>> 方法(PublisherBuilder<Message<I>> pub)

  • Publisher<O> 方法(Publisher<I> pub)

  • PublisherBuilder<O> 方法(PublisherBuilder<I> pub)

现在,我们将查看一些使用这些签名构建消息处理链的示例。

微 Profile 反应流操作符

反应式流不仅仅是将发布者连接到订阅者。通常,一个流需要以某种方式进行操作,比如应用包括 mapfilterflatMap 的操作。反应式流和 JDK 都没有提供执行这些操作的 API。由于用户不需要自己实现反应式流,这意味着目前进行这些操作的唯一方式是依赖于第三方库提供操作符,如 Akka Streams、RxJava 或 Reactor。

MicroProfile 反应式流操作符 API 旨在填补这一空白,使得 MicroProfile 应用开发者能够无需引入第三方依赖即可操作反应式流。...

MicroProfile 上下文传播

这个规范位于 MicroProfile 平台发布之外的范畴,目前仍处于提议或草稿状态。我们将在本章后面的 MicroProfile 未来发展趋势 部分更详细地讨论这个规范,但在这里我们先给出一个高层次的介绍。

MicroProfile 上下文传播规范引入了 API,用于在无线程关联的工作单元之间传播上下文。它使得能够将传统与当前线程相关联的上下文传播到各种工作单元,如 CompletionStageCompletableFutureFunctionRunnable,无论最终是哪个特定的线程执行它们。

MicroProfile 反应式消息示例

在本节中,我们将介绍一些使用 MP-RM 创建产生和消费消息的 CDI 豆子的示例。

假设你想让一个 CDI 豆子作为 Message<String> 的来源,这样每当调用其 publishMessage(String) 方法时,就会向某个 MP-RM 通道发布 MP-RM 消息。为此,我们需要定义一个连接器,它将 CDI 豆子与 MP-RM 层连接起来。下面代码示例展示了一个实现此功能的传入消息连接器:

package io.pckt.reactive;import javax.enterprise.context.ApplicationScoped;import javax.inject.Inject;import org.eclipse.microprofile.config.Config;import org.eclipse.microprofile.reactive.messaging.Message;import org.eclipse.microprofile.reactive.messaging.spi. ...

MicroProfile 未来发展趋势

如第二章 治理和贡献 中所述,带到 Eclipse MicroProfile 项目的新想法首先在 MicroProfile 沙箱中尝试,遵循实现优先的创新方法。沙箱练习为实施者和社区提供了讨论、分析和评估新想法如何融入 MicroProfile 项目的机会。如果在沙箱练习结束时,社区认为这个新想法值得添加到项目中,就会为它创建一个特定的 MicroProfile 子项目。子项目在至少发布一个版本之前,才能考虑加入到 MicroProfile 伞/平台发布中。在非常高层次上,这是新想法和未来发展趋势在 MicroProfile 项目下遵循的过程。

在下一节中,我们将讨论两种类型的项目,这些项目目前是 MicroProfile 的子项目,目前不在 MicroProfile 伞下/平台发布(将这些视为已经从 MicroProfile 沙箱毕业的项目),以及仍在 MicroProfile 沙箱中的项目。最后,我们将讨论 Eclipse MicroProfile 和 Jakarta EE 之间的当前关系,以及它们的路线图可能满足也可能不满足。

伞下项目

在本节中,我们将介绍在 Eclipse MicroProfile 伞下发布之外的项目,当然,在撰写本文时。这些如下:

  • 响应式流操作符

  • 响应式消息传递

  • 长期运行动作

  • 上下文传播

  • GraphQL

本章的前几节已经讨论了响应式流操作符和响应式消息传递项目,因此在本节中我们只覆盖长期运行动作、上下文传播和 GraphQL。

长期运行动作

在松耦合的服务环境中,长期运行动作LRA)规范背后的动机是为许多微服务的调用组成的业务流程提供一致的结果,而无需锁定数据。思考 LRA 的一种方式是将其视为微服务的事务。需要 LRA 的情况包括以下几种:

  • 在网上订购一本书将需要从库存中退役一本书,处理支付,最后是发货。所有这些任务都需要原子性地完成,换句话说,它们需要一起处理,也就是说,如果任何任务失败,那么所有任务都必须被撤销。

  • 预订航班将需要从飞机可用的座位列表中移除一个座位,为旅客选择并分配一个特定的座位,处理支付,以及创建一个记录定位器。同样,所有这些任务必须在同一个长期运行动作中完成。

不仅上述示例需要原子性地完成,而且它们还需要生成一个结果,即使它们的中间步骤中有任何失败,数据也是一致的。

微概要 LRA 当前提出的解决方案受到了OASIS Web 服务组合应用框架技术委员会的启发(www.oasis-open.org/committees/tc_home.php?wg_abbrev=ws-caf),即Web 服务**长期运行动作事务模型www.oasis-open.org/committees/document.php?document_id=12794),但已更新以更适用于微服务架构。

有关 MicroProfile LRA 规范的更多信息,请参考github.com/eclipse/microprofile-lra/blob/master/spec/src/main/asciidoc/microprofile-lra-spec.adoc

MicroProfile 长期运行动作规范模型包括三个主要实体:补偿器、逻辑协调器和客户端。客户端可以通过两种不同的方式显式启动新的 LRA:

  • 通过注解,或者

  • 通过 API 调用

要么创建一个新的 LRA。如果一个服务可能需要稍后撤销某事,那么客户端需要为 LRA 注册一个补偿器。如果客户端选择关闭或取消 LRA,补偿器将撤销服务在 LRA 范围内执行的工作,或者对未完成的工作进行补偿。

以下是一些主要的 LRA 注解:

  • @LRA控制 LRA 的生命周期。

  • @Compensate表示如果 LRA 被取消,则应调用该方法。

  • @Complete表示如果 LRA 关闭,则应调用该方法。

  • @Forget表示该方法可能释放为这次 LRA 分配的任何资源。

  • @Leave表示这个类不再对这次 LRA 感兴趣。

  • @Status在调用注解方法时报告状态。

你可以将这些注解与 JAX-RS 和非 JAX-RS 方法一起使用。此外,该规范支持 JAX-RS 的异步和反应式特性,LRA 嵌套和超时功能。最后,值得一提的是,LRA 规范通过对接口参与实体提出某些要求,确保事务性和最终一致性。作为一个 MicroProfile 项目,MicroProfile LRA 规范,在撰写本文时,处于提议或草稿状态。

上下文传播

MicroProfile 上下文传播规范的目标是在线程无关的工作单元之间传播上下文。在一个反应式模型中,逻辑的执行被拆分为相互链式组装的反应式流水线的工作单元。每个工作单元在上下文中执行,这通常是不可预测的,并取决于正在使用的特定反应式引擎。一些单元可能在与线程等待完成上下文的情况下运行,或者在完成并触发依赖单元的前一个单元的上下文中运行,或者根本没有任何上下文。MicroProfile 上下文传播规范使得线程上下文传播可以轻松地以类型安全的方式完成,保持了样板代码...

GraphQL

GraphQL 是一个开源的数据查询和操作语言,用于 API,以及用于用现有数据满足查询的运行时。它解释客户端的字符串并以可理解、可预测和预定义的方式返回数据。GraphQL 是 REST 的替代方案,但不一定是替代品。MicroProfile GraphQL 规范的目标是为用户提供一套 API,使用户能够快速在 Java 中开发可移植的 GraphQL 基础应用程序。作为一个 MicroProfile 项目,MicroProfile GraphQL 规范在撰写本文时,处于提议或草稿状态。

GraphQL 基于 Facebook 的规范。关于这一点,请参阅https://graphql.github.io/graphql-spec。有关 GraphQL 的更全面概述,请访问graphql.org/

GraphQL 和 REST 有许多相似之处,都在现代微服务应用中得到广泛使用。

GraphQL 与 REST 的区别

以下是与 REST 相比 GraphQL 的主要区别性特征:

  • 基于架构:架构作为服务器及其客户端之间的合同。

  • 单一 HTTP 端点:通过查询语言实现单一端点和数据及操作的访问。

  • 灵活的数据检索:允许客户端以细粒度的方式选择响应中的数据,从而避免过度或不足地获取数据。

  • 减少服务器请求:该语言允许客户端将预期的数据聚合成单个请求。

  • 更简单的版本管理:在创建新数据的同时,使旧数据过时。

  • 部分结果:结果由数据和错误组成。客户端负责...

GraphQL 和数据库

GraphQL 不是数据库技术。相反,它是用于 API 的数据查询和操作工具,不依赖于任何数据库或存储技术。但是,它可以用于任何后端之前,并能够通过单个 API 从多个后端数据源聚合数据。

沙盒中的项目

MicroProfile 项目沙盒是社区可以提出想法的地方,通过尝试实现功能和特性的实现,从社区成员那里获得反馈、讨论和评估,以决定是否将想法变成 MicroProfile 项目的新 API/规范。

MicroProfile 沙盒位于github.com/eclipse/microprofile-sandbox

从沙盒毕业成为正式 MicroProfile 项目的过去项目建议包括 GraphQL 和 Reactive Streams Operators。在撰写本文时,沙盒中只有一个建议项目,即 Boost。

MicroProfile Boost

在撰写本文时,MicroProfile Boost 在 MicroProfile 沙盒中处于社区评估阶段。Boost 是一个 Maven 插件,可增强为您的 MicroProfile 应用程序构建的过程。

有关 Boost 的更多信息,请访问github.com/eclipse/microprofile-sandbox/tree/master/proposals/boost

Boost 为 MicroProfile API 定义了 Maven 依赖项,称为启动器,例如为 MicroProfile Config 定义的mpConfig,以及为 Java EE API。此外,它还为实现不同 MicroProfile API 的运行时定义了依赖项,例如openliberty。还有一个作为 BOM(物料清单)指定的 Boost 定义的 Maven 依赖项,它指示在构建有关 MicroProfile API 的 maven 时使用 MicroProfile 伞项目的哪个版本。BOM 内容由插件管理。作为 Boost 的用户,您将把这些依赖项包含在您的pom.xml文件中,以简化 MicroProfile 应用程序的构建过程。

Eclipse MicroProfile 和 Jakarta EE

Eclipse Jakarta Enterprise Java 项目和 Eclipse MicroProfile 项目之间的关系是什么?简而言之:尚未可知。详细答案:让我们开始。

Eclipse MicroProfile 项目旨在解决在Java Community ProcessJCP)下 Java EE 规范缺乏进展的问题。

有关 Java 社区过程的更多信息,请访问https://jcp.org/en/home/index

自 MicroProfile 项目移至 Eclipse 基金会以来已经过去了两年多时间。大约一年后,Oracle 宣布计划将 Java EE 移至 Eclipse 基金会并将其重命名为 Jakarta EE。移至 Jakarta 的过程是一个漫长的过程,而且还在继续...

摘要

在本章中,我们了解了 MicroProfile 规范的未来发展,包括 Long Running Actions、Context Propagation 和 GraphQL 项目,它们都不在伞发布之外,以及仍在 MicroProfile 沙箱中的 Boost 项目。此外,您还了解了反应式消息传递的概念、MicroProfile 反应式消息传递架构,以及如何使用代码示例通过 Eclipse MicroProfile 反应式构件实现反应式微服务。您还获得了这些项目的背景知识、它们的 capabilities、注解(annotation)和在适用情况下的代码示例,以及它们当前的状态。最后,我们介绍了两个类似但不同的项目:Eclipse MicroProfile 和 Jakarta EE 之间的关系,并讨论了它们可能在未来如何发展。

在下一章中,我们将学习在多云环境和部署中关于 Eclipse MicroProfile 的知识。

问题

  1. 如果我们有一个消息源,我该如何将其集成到我的 MicroProfile 应用程序中?

  2. MicroProfile Context Propagation 最佳支持现有 MicroProfile 规范中的哪一个?

  3. 目前支持反应式编程的 MicroProfile 规范有哪些?

  4. 目前哪些 MicroProfile 规范位于伞/平台 MicroProfile 发布之外?

  5. 为什么要有 MicroProfile 沙盒呢?

  6. 目前哪些项目位于 MicroProfile 沙盒中?

  7. 当前 Eclipse MicroProfile 与 Jakarta EE 之间的关系是什么?

  8. 未来 Eclipse MicroProfile 与 Jakarta EE 之间的关系将如何发展?

进一步阅读

  • 对于 MicroProfile 响应式消息传递,reactivex.io/网站提供了动机、教程、语言绑定等内容。

  • 学习 GraphQL 的一个好起点是graphql.org/网站,该网站提供了更多关于其背后的动机,以及许多用于探索如何使用它的资源。

第十一章:在多云环境中使用 MicroProfile

微服务和微服务架构是云和多云环境的理想开发方法,包括混合云部署,您的应用程序包括本地逻辑以及运行在云中的逻辑。Eclipse MicroProfile 是一个优化 Java 适用于微服务架构的规范,因此提供了构造,使您可以使用 Java 和云实现微服务。这些主题将帮助您了解为什么 Eclipse MicroProfile 适用于开发混合和多云环境中的应用程序,以及在这些类型的部署中使用它时必须考虑的因素。

在本章中,我们将讨论以下内容...

使用 Eclipse MicroProfile 进行云原生应用开发

什么是云原生应用?通常,对于云原生的定义包括以下特征:

  • 设计为松耦合服务,如微服务

  • 松耦合的服务,通过语言无关的通信协议进行交互,这使得微服务可以采用不同的编程语言和框架实现

  • 可以按需或通过资源利用率指标进行扩展轻量级容器

  • 通过敏捷 DevOps 流程管理,每个云原生应用程序的微服务都经历一个独立的生命周期,该生命周期通过使用持续集成/持续交付CI/CD)管道进行敏捷管理

然而,Eclipse MicroProfile 的目标是优化 Java 适用于微服务架构,那么它是否适合云原生应用开发呢?容器原生开发又如何?微服务、云原生开发和容器原生开发之间的关系是什么?它们之间有什么区别或比较?让我们来找出答案!

微服务与云原生与容器原生

首先,让我们绘制这三个术语在应用程序开发方面的区别。正如我们在第一章中讨论的,Eclipse MicroProfile 简介,企业 Java 微服务具有以下特征:

  • 这是一个使用 Java 语言编写的微服务。

  • 它可以使用任何 Java 框架。

  • 它可以使用任何 Java API。

  • 它必须是企业级的,这意味着它必须具有高可靠性、可用性、可扩展性、安全性、健壮性和性能。

  • 它必须满足微服务的所有特征,这些特征列在martinfowler.com/microservices/上。

根据其定义,微服务不规定底层的具体细节...

那 12 因子应用又如何呢?

就像微服务和微服务架构的定义一样,12 因子应用不会规定底层技术,例如编程语言、数据库、缓存等,或应使用它们实现的框架。12 因子应用是一种用于实施应用程序的方法。这十二个因素如下:

  • 一个代码库在版本控制中跟踪,多个部署

  • 明确声明和隔离依赖项

  • 在环境中存储配置

  • 将后端服务视为附加资源

  • 严格区分构建和运行阶段

  • 将应用程序作为无状态进程之一或多个执行

  • 通过端口绑定导出服务

  • 通过进程模型进行扩展

  • 通过快速启动和优雅关机最大化健壮性

  • 尽可能保持开发、暂存和生产环境相似

  • 将日志视为事件流

  • 将管理任务作为一次性流程运行

使用这种方法实施应用程序可以帮助我们做到以下几点:

  • 最小化新开发者加入项目的时间和成本

  • 提供在执行环境之间可移植性

  • 轻松将应用程序部署到云平台

  • 最小化开发和生产之间的差异

  • 无需更改即可扩展

您可以在12factor.net上阅读到关于 12 个因素的全部内容。

12 因子应用是一种开发者可以在设计和实现微服务和应用程序时遵循的方法,与实现它们的编程语言或框架无关。开发者可以使用 12 因子应用框架实现微服务,这个框架是 Eclipse MicroProfile。12 因子应用和 Eclipse MicroProfile 并不是相互排斥的,而是相辅相成的。

但是,除了 12 因子应用之外,还有没有其他设计和实现应用程序的方法呢?无服务器和功能即服务FaaS)技术又如何呢?Eclipse MicroProfile 在这些更新的云原生技术中如何定位?

那么无服务器和 FaaS 呢?

无服务器和 FaaS 作为云原生技术,在市场上一直保持着稳定的兴趣和增长,这一点从所有主要云服务提供商的产品中可以看出,即 AWS Lambda、Azure Functions、Oracle Functions 和 Google Cloud Functions。在组织越来越多地使用云进行开发和生产负载的时代,计算和内存成本是必须跟踪和监控的操作费用,FaaS 之所以吸引人,是因为它将计算和内存管理从用户手中抽象出来,使用户能够专注于开发业务逻辑,从而比以往任何时候都更加高效。

使用 FaaS,开发人员无需设置虚拟机和内存,...

云原生应用开发

云原生应用开发有两个互补的方面或组件:应用服务和基础设施服务。应用服务加快了云原生应用的业务逻辑开发,而基础设施服务则加快了其交付和部署。这两个方面是互补的,并构成了云原生应用开发的一部分。没有另一个,你无法拥有。它们本质上是云原生应用开发的阴阳两面,如下面的图表所示:

正如我们在这章节前面提到的,云原生应用开发是一种构建和运行应用程序的方法,它充分利用了云计算模型,该模型基于四个核心原则:

  • 基于服务架构(微服务、微服务、SOA 服务等)

  • 服务间通信的 API 驱动方法

  • 基于容器的底层基础设施

  • DevOps 流程

下面的图表展示了云原生应用开发的四个核心原则:

如图所示,前一个图表中,架构和通信方面与云原生应用的开发关注点相关,而基础设施和流程方面则与它们的交付/部署相关。

正在采用云原生应用开发的组织可以从八个步骤中受益,这些步骤在电子书中有所描述,书名为 The path to cloud-native applications: **8 steps to guide your journey.

要获取电子书 The path to cloud-native applications: **8 steps to guide your journey, 请参考 www.redhat.com/en/resources/path-to-cloud-native-applications-ebook.

让我们讨论一下 Eclipse MicroProfile 在这八个步骤中可以发挥什么作用:

  1. 发展和实践 DevOps 文化:“利用新技术、更快的方法以及更紧密的协作,通过采纳 DevOps 的原则和文化价值观,围绕这些价值观组织你的组织.” 尽管这是一个与组织和工作流程相关的步骤,但作为微服务规范的 Eclipse MicroProfile 可以很好地适应这种文化和流程的改变,因为微服务由于其特性,紧密支持 DevOps 流程。

  2. 使用快速单体加速现有应用:“通过迁移到现代、基于容器的平台来加速现有应用——并将单体应用分解为微服务或小型服务,以获得额外的效率提升。” 在将单体应用分解为微服务时,Eclipse MicroProfile 可以提供很大帮助。 当你在单体应用中识别出边界上下文时,可以考虑使用 Eclipse MicroProfile 来实现每个微服务,这些微服务实现每个边界上下文的逻辑。

  3. 使用应用服务加快开发速度:“通过可重用性加快软件开发。云原生应用服务是即用的开发者工具。然而,这些可重用组件必须优化并集成到云原生基础架构中,以最大化其效益。内存数据网格IMDG)和消息代理是应用服务,它们有助于加快业务逻辑的开发。使用 Eclipse MicroProfile 开发的微服务,可以通过其方法体内部调用这些应用服务。Eclipse MicroProfile 在集成应用服务,如 IMDG 或消息代理时,不会施加任何限制。

  4. 为每项任务选择合适的工具:“使用基于容器的应用平台,支持正确的框架、语言和架构的混合——并可根据您的特定业务应用需求进行定制。” 当选择合适的工具来完成任务时,开发者可以使用 Eclipse MicroProfile 这样的工具。例如,Red Hat Application Runtimes 是一组运行时和工具的集合,其中包括 Eclipse MicroProfile、Node.js、Spring Boot 和 Vertex。

  5. 为开发者提供自助、按需的基础设施:“使用容器和容器编排技术简化对底层基础设施的访问,赋予 IT 运营团队控制和可见性,并在各种基础设施环境中,如数据中心、私有云和公有云,提供健壮的应用生命周期管理。” 使用 Eclipse MicroProfile 开发的微服务可以部署到一个或多个容器中。 通过轻松管理这些容器以及在其上运行的微服务架构,您可以加快开发周期,更快地向业务交付价值。

  6. 自动化 IT 以加速应用交付:“创建自动化沙盒,以学习自动化语言和流程,跨组织建立协作对话以定义服务需求,创建自助服务目录以赋予用户权力并加快交付,以及使用度量、监控和计费回策略和流程。” Eclipse MicroProfile 提供了度量、容错和健康检查等功能,这些都可以作为 IT 自动化过程的输入。

  7. 实施持续交付和高级部署技术:"使用自动化交付、CI/CD 管道、滚动蓝/绿和金丝雀部署以及 A/B 测试,加速你的云原生应用程序的交付。" 微服务与 CI/CD 的结合可以促进高级部署技术。例如,你可以作为一个蓝/绿或金丝雀部署的一部分,将具有新功能的 MicroProfile 基础微服务引入生产中,一旦你证明了新的功能如预期般工作,就可以将所有流量切换到它。

  8. 进化成更模块化的架构: "选择一种适合你特定需求的模块化设计,使用微服务、单体优先方法或迷你服务——或它们的组合。" 在这一步,你可以使用 Eclipse MicroProfile 来为新的应用程序开发微服务,或者在你将单体的特定有限上下文拆分为微服务时使用。

既然我们已经讨论了 Eclipse MicroProfile 如何促进云原生应用程序的开发,以及它如何在八个步骤中帮助引导你走向云原生应用程序,现在让我们转向在云之间运行基于 MicroProfile 的应用程序的主题。

在云之间开发和运行 MicroProfile 应用程序

MicroProfile 提供了哪些特性来支持在云之间开发?微服务和基于 HTTP REST API 的语言无关通信是支持的两个主要特性。此外,MicroProfile Config 支持将云环境变量集成到定义与云环境集成的集成中。MicroProfile Health Check 支持与云环境健康检查的集成。MicroProfile Metrics 和 MicroProfile OpenTracing 支持与 DevOps 监控任务的集成。最后,MicroProfile 容错支持独立微服务之间的回退和恢复行为。

Eclipse MicroProfile ...

裸机机器与虚拟机与容器

是否在裸机机器、虚拟机或容器上运行基于 MicroProfile 的微服务或应用程序取决于你的应用程序的具体要求。实际上,确定哪种底层云计算资源完全取决于你的应用程序需求,而不是用于其开发的框架,即 Eclipse MicroProfile。例如,如果你的应用程序或微服务需要实时或接近实时的响应时间,那么你很可能会偏好裸机或容器(运行在裸机上)部署。这个决定与你正在使用来编写业务逻辑的框架无关,无论是 Eclipse MicroProfile 还是其他框架。

由于 Eclipse MicroProfile 支持微服务和基于 HTTP REST 的语言无关通信,你的微服务之间的通信不受运行微服务的底层计算类型的影响;例如,你可以有一个运行在 VM 上的微服务通过 REST 与另一个运行在裸机上的微服务进行通信。但是,如果你的应用程序由在本地运行的微服务和在云上运行的另一个微服务组成,也称为混合云应用程序,你需要考虑什么?

在混合云部署中使用 MicroProfile 时的考虑

混合云应用程序包括本地逻辑以及云逻辑。换句话说,如果你的应用程序逻辑的一部分在本地运行,另一部分在云中运行,你实际上拥有一个混合云应用程序。当在这个类型的部署中使用 Eclipse MicroProfile 时,以下是你需要考虑的事情:

  • 配置云环境与本地环境之间的通信路由需要使用云环境支持的所有 DNS 支持

  • 配置 MicroProfile OpenTracing 以在云环境之间捕获跟踪

  • 在云环境和本地环境之间分割 MicroProfile Metrics 信息进行监控...

在多云部署中使用 MicroProfile OpenTracing 时的挑战

在多云环境中进行分布式跟踪可能会很有挑战性。我们希望能够实现与单一云环境相同的目标,即可视化与请求相关联的单个端到端跟踪,以及它穿过每个云中的服务,但在处理不同的上下文传播格式和每个云存储跟踪数据的不同格式时可能会遇到复杂问题。

第一个挑战是要确保跟踪可以在不同的云环境中持续进行。这是一个问题,因为截至撰写本书时,还没有被广泛采用或标准化的跟踪上下文格式。通常,每个跟踪系统都使用不同的头文件和格式来传播跟踪上下文。例如,Zipkin 使用 B3 传播,Jaeger 使用 ber-trace-id 头文件,Amazon X-Ray 使用 X-Amzn-Trace-Id,Google 使用 X-Cloud-Trace-Context。因此,如果一个请求需要在不同的跟踪系统之间进行跟踪,每次它离开或进入不同的环境时,都需要转换跟踪上下文。这通常可以通过配置带有自定义注入器或提取器实现的跟踪器来完成。然而,这目前超出了 MicroProfile OpenTracing 项目的范围。将来,跟踪上下文格式可能会在 W3C 跟踪上下文项目下得到标准化(www.w3.org/TR/trace-context/).

第二个挑战,即使在同质追踪环境中,也是可视化多云环境中的追踪数据。这可能很成问题,因为每个云中的追踪数据可能存储在不同的数据库或不同的格式中。这可以通过将数据复制到单一统一存储,或者使用适当的数据格式调整,在系统之间按需发送缺失的追踪数据来克服。

接下来,我们将讨论在像 Istio 这样的服务网格中使用 Eclipse MicroProfile 遇到的挑战。

在服务网格中使用 Eclipse MicroProfile 的考虑

像 Istio 或 LinkerD 这样的服务网格在 Kubernetes 之上的平台级别提供服务,涵盖发现、路由和故障容限等领域。其中一些服务也可以在 MicroProfile 中找到。当你将 MicroProfile 应用程序部署在这样的服务网格中时,你需要考虑是否想使用 MicroProfile 版本或网格中的版本。

受影响的 MicroProfile 特性最可能是故障容限,尤其是重试逻辑。

重试

在故障容限中的重试允许在第一个请求失败时重试对另一个服务的请求(更多信息请参见第三章,MicroProfile Config 和 Fault Tolerance)。现在,考虑你有以下代码:

@Retry (maxRetries = 3)
void aMethod() {
    callBackend();
}

尽管这告诉 Istio 重试 5 次,但最终可能导致aMethod以错误结束,共进行 15 次重试(Istio 会在返回错误之前为您的代码中的 3 次重试各重试 5 次)。你可能考虑关闭代码中的重试,因为可以通过不需要重启 pod 的方式,随时在 Istio 中更改重试次数。

回退

另一方面,Istio 没有在所有重试失败时调用回退策略——不可能让 Istio 调用您工作负载的另一个版本。当你在前面的代码上添加@Fallback注解时,在原始调用失败的情况下可以执行另一个动作:

@Fallback(fallbackMethod = "fallbackForA")@Retry (maxRetries = 3)string aMethod() {   callBackend();}void String fallbackForA() {    return "A cached string";}

在这种情况下,一旦 Istio 和 MicroProfile 的所有重试都耗尽,将会调用fallbackForA回退方法。如果你从前面示例中移除了@Retry注解,当 Istio 的重试耗尽时会调用回退方法...

服务网格中的故障注入

Istio 可以很容易地在结果中注入故障。这听起来开始时有些适得其反,但这是一个非常好的测试您是否正确处理了故障容限的方式。以下是为 Istio 定义故障注入的VirtualService

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: fault-injection-service
spec:
  hosts:
  - myTarget
  http:
  - route:
    - destination:
      host: myTarget
    fault:
      abort:
        httpStatus: 404
        percent: 20
     delay:
       percent: 50
       fixedDelay: 200ms

Istio 将监听对目标主机myTarget的调用,并对 20%的调用发送 404 响应,而不是真实的响应代码。此外,它还会将其他每个响应延迟 200 毫秒。

结论

MicroProfile 在容错领域定义了原语,这些原语也可以通过其他方式提供,例如在服务网格中。如果这是你的情况,你必须考虑激活哪一个。同时激活两个可能会导致意外行为。

总结

在本章中,你已经学习了 Eclipse MicroProfile 如何促进云原生应用程序开发,无论是混合云还是多云应用程序。我们还讨论了微服务、云原生开发和容器原生开发之间的关系,以及运行在容器上的微服务是云原生开发的理想选择。你也学习了 Eclipse MicroProfile 与 12 因子应用程序以及无服务器和函数即服务环境的关系。

我们覆盖了八个步骤,指导你进入云原生应用程序的旅程,以及 Eclipse MicroProfile 如何在每一步帮助。此外,我们还讨论了在使用 Eclipse MicroProfile 跨云应用程序时需要考虑的内容,何时在裸机机器上运行 Eclipse MicroProfile 与在虚拟机上运行 Eclipse MicroProfile 与在容器上运行 Eclipse MicroProfile,以及在运行 Eclipse MicroProfile 微服务时需要考虑的内容在混合云应用程序中,在运行 Eclipse MicroProfile OpenTracing 时需要意识到的多云部署挑战,以及最后在使用 Eclipse MicroProfile 时需要考虑的内容在服务网格中。

在这本书中,我们已经介绍了 MicroProfile 的起源,这是一个针对 Java 微服务的规范,以及它背后的历史。我们介绍了开源项目,它的使命、治理、好处、如何为其做出贡献以及其子项目的生命周期。然后我们深入探讨了构成伞式/平台发布的每个 MicroProfile API/子项目,以及不在伞式发布的子项目。

我们同样覆盖了市场上当前的 MicroProfile 实现,包括 Quarkus,并展示了如何使用 MicroProfile Starter 生成的 MicroProfile 项目来“quark”项目。我们讨论了会议应用程序,这是一个社区样本项目,展示了 MicroProfile 在不同供应商实现之间的互操作性。我们还在整个文档中提供了代码示例,以便参考和讨论,并且还提供了一个带有源代码的完整可工作项目,实现了所有 MicroProfile API,您可以自由访问、下载、编译并在您的开发努力中重用,以及开始使用 MicroProfile。后来,我们讨论了 MicroProfile 提供的响应式功能及其未来发展,例如管道中的子项目和对 MicroProfile 沙盒的子项目,以及它可能与 Jakarta EE 的未来关系。

最后,我们讨论了基于MicroProfile的应用程序和微服务如何适合在容器、云以及无服务器/FaaS 部署中实现解决方案。无论您是 Java 微服务的新手还是有经验的 Java 开发者,您都可以利用本书中获得的所有知识来开始开发基于这一新型的、创新的社区驱动规范的微服务,以创建可观察、可扩展、安全和高可用的混合和多云应用,从而您可以更快地为您的企业提供价值。

问题

  1. Eclipse MicroProfile如何促进云原生应用开发?

  2. 云原生应用开发的两个互补关注点是什么?Eclipse MicroProfile如何融入这些关注点?

  3. 云原生应用开发的四个关键原则是什么?Eclipse MicroProfile与这些原则有何关联?

  4. Eclipse MicroProfile如何为引导您通过云原生应用开发的八个步骤做出贡献?

  5. Eclipse MicroProfile与 12 因子应用有何关联?

  6. Eclipse MicroProfile如何促进无服务器和 FaaS 环境?

  7. 在跨云实施和运行Eclipse MicroProfile时,您应该考虑哪些问题?

  8. 使用Eclipse MicroProfile时会遇到哪些挑战...

第十二章:评估

第一章

  1. 企业 Java 微服务具有以下特性:

    • 它是使用 Java 语言编写的。

    • 它可以使用任何 Java 框架。

    • 它可以使用任何 Java API。

    • 它必须是企业级的:可靠、可用、可扩展、安全、健壮和性能良好。

    • 它必须满足微服务架构的特性,如martinfowler.com/microservices/所列,这些特性如下:

      • 通过服务实现组件化

      • 围绕业务能力组织

      • 产品而非项目

      • 智能端点和愚蠢的管道

      • 去中心化治理

      • 去中心化数据管理

      • 基础设施自动化

      • 设计失败

      • 进化式设计

  2. 数字经济和数字转型的术语描述了四个不同力量的汇合...

第二章

  1. 微 Profile 社区的主要沟通方式是他们的 Google 组,称为Eclipse MicroProfile。您可以通过向microprofile@googlegroups.com发送电子邮件来发送消息给它。另一种很好的方式来让你的声音被听到是参加每两周一次的 MicroProfile Hangout 通话。请查看 MicroProfile Google 日历以获取确切的日期、时间和会议信息以加入。

  2. 微 Profile Hangout 通话作为一个论坛,讨论与会者提出的话题并做出决定,包括子项目状态、发布内容、发布日期和子项目创建批准。

  3. 子项目(MicroProfile 规范)负责人或一组负责人通常是所在主题的专家,并指定为其提供便利。需要指出的重要一点是,工作组的负责人(或子项目)并不是单独塑造或决定规范的演变,或者包括哪些功能以及不包括哪些功能。他们没有否决权或在有关其规范的决策上没有最终决定权。通过分享观点、专业知识、过去经验、现有技术的分析和最佳实践,工作组将提出他们最好的建议。

  4. 在 MicroProfile Google 组和/或 MicroProfile Hangout 通话中详细讨论新想法之后,如果确定值得进一步辩论,社区决定为这个新想法创建一个工作组,并指定一个负责人或一组负责人,他们通常是处理主题的专家,担任其促进者。工作组将建立一个定期每周或每两周的会议,并记录在 MicroProfile Google 日历中。任何人都可以参加会议,但通常有一小群人作为主题专家参加这些电话会议。经过几次会议,工作组决定是否将新功能带到 MicroProfile Hangout 电话中讨论其成为 MicroProfile 子项目的提案。在 MicroProfile Hangout 电话中,子项目提案可能会被拒绝或接受。接受子项目意味着它有效地解决了一个需求,丰富了规范,使其朝着优化企业 Java 以适应微服务架构的目标迈进。此刻,子项目成为正式的 MicroProfile API。一旦子项目成为 MicroProfile API,然后决定它是否应该作为独立子项目在外部或作为 MicroProfile 发布伞下的子项目。

  5. Eclipse MicroProfile 遵循时间盒的快速逐步发布计划,该计划是公开的,列在 Eclipse 基金会 MicroProfile 项目页面上。例如,从 1.x 到 2.x 的主要 Eclipse MicroProfile 发布,包括对 MicroProfile API 的重大更新,可能会引入破坏性变化。次要发布,即点发布,包括对小型 API 的更新或新的 API,以确定发布日期。目前,MicroProfile 社区的发布窗口每年在二月、六月和十一月,适用于次要和/或主要发布。

  6. 沙盒仓库是一个 GitHub 仓库,用于孵化最终将成为独立仓库的想法和代码示例,为新的规范做出贡献。任何人都可以打开拉取请求,并使用沙盒进行新想法的实验,以及分享代码和文档,这些可以作为社区 Google 组、MicroProfile Hangout 通话或工作组会议讨论的一部分使用。

  7. 将子项目发布在 MicroProfile 伞下/平台之外的原因是,它给了社区和最终用户一个机会来使用和测试新技术,因此在将其纳入伞下之前,在真实应用中证明它。MicroProfile 项目鼓励在将其纳入伞下/平台发布之前,新的子项目规范至少在外部发布一个版本。

  8. 微服务启动器是一个样本源代码生成器,其目标是帮助开发者快速开始使用和利用企业 Java 微服务社区驱动的开源规范 Eclipse MicroProfile,通过在 Maven 项目中生成工作样本代码。

第三章

  1. 配置属性的默认来源是环境变量、Java 系统属性和META-INF/microprofile-config.properties文件。

  2. 你可以提供一个自定义 ConfigSource 实现,该实现将属性名称映射到自定义源中的值。

  3. 字符串并不是唯一支持的数据类型,因为 MP-Config 通过可插拔的 SPI 支持类型转换,并且默认提供几种转换。

  4. 如果你已经给了一个defaultValue,或者已经将属性作为Optional<?>值注入,你不需要为注入的属性提供一个值。

  5. 复杂的属性类型可以使用自定义Converter<?>实现来处理,该实现接受一个字符串并返回复杂类型。

  6. 当在类上指定一个注解时...

第四章

  1. 线缆格式在只查看 HTTP 状态码来确定 HC 状态的环境中不可用。

  2. 一个 MP-HC 响应可以通过使用HealthCheckResponse#withData()方法包括任意属性。

  3. 你可以为每个服务创建一个 HealthCheck 实现,MP-HC 功能将逻辑上组合它们以产生总体状态响应。

  4. JWT 是一个 JSON Web Token,一个具有头部、载荷和签名组件的 JSON 格式的对象。

  5. 声明是 JWT 载荷中的一个单独命名的值。

  6. 任何可以表示为 JSON 的东西都可以用在 JWT 中。

  7. 验证 JWT 的一个主要步骤是验证它是否基于配置的公钥使用 RS256 算法进行签名。

  8. 可以查看除了组声明以外的其他声明来添加应用程序特定的授权决策。

第五章

  1. 分布式跟踪提供了从端到端请求的微观视图,而指标则暴露了单个组件的标量数值。

  2. 分布式跟踪系统通常提供诸如根因分析和关键路径分析、上下文化日志记录、分布式上下文传播和服务依赖关系图等功能。

  3. 自动跟踪 JAX-RS 服务器端点和 MicroProfile Rest 客户端。一些供应商还可以自动跟踪 JAX-RS 客户端。

  4. 这些标签对每个 REST 请求http.methodhttp.status_codehttp.urlcomponentspan.kinderror(如果抛出异常)进行添加。

  5. 可以通过使用@Traced注解或注入追踪器并创建...来实现显式 instrumentation。

第六章

  1. 不是的:默认情况下,即使没有使用任何一个 MP OpenAPI 注解,任何 REST 端点都会有 OpenAPI 生成。

  2. 是的:你可以选择使用多少个或多少少的 MP OpenAPI 注解来表示你的微服务中的 REST 端点。

  3. 这种观念是你预先定义好你的端点的预期合同,并将其封装在 OpenAPI 文档中,这些文档可以与你的微服务一起打包。

  4. 不需要:你只需要知道请求和响应的格式,然后你就可以创建你自己的类型安全接口。

  5. 通过使用.../mp-rest/url MP Config 设置,其中...是类型安全接口的接口名或传递给RegisterRestClient注解的 configKey。

  6. 一种方法是注册一个ClientHeadersFactory实现。另一种方法是将标头列在org.eclipse.microprofile.rest.client.propagateHeaders MP-Config 属性中。

第七章

  1. 在撰写本书时,有八个 Eclipse MicroProfile 的实现,全部都是开源的。它们是 Thorntail、Open Liberty、Apache TomEE、Payara Micro、Hammock、KumuluzEE、Launcher 和 Helidon。还有 Quarkus 作为最新加入者。

  2. 应用服务器是 Java EE 应用程序的容器。应用组装器只包括应用程序需要的功能,而不是要求应用服务器运行,并且通常生成可执行的 JAR 文件。应用组装器可以生成 uberjar,一个自包含的可运行 JAR 文件,或者一个带有其运行时依赖位于子目录中的应用 jar,例如,伴随的liblibs子目录。...

第八章

  1. 我们在整本书以及本章中看到了许多示例,MP-Config 设置会影响应用程序和 MP 特性。

  2. 只要你提供的路径存在,你应该能够看到带有关于该路径信息的成功的健康检查。

  3. 它显示了关于 KeyCloak 服务器的信息。如果 KeyCloak 停止了,它会显示一个错误。

  4. 因为它是在第一次请求时延迟生成的,所以找不到这个指标。

  5. 待定。

  6. 对于未加密的方法,行为应该相似。对于加密的方法,Swagger-UI 调用失败。

  7. 你会看到错误响应。

  8. 那就是编码后的 MP-JWT。你可以在 curl 命令中使用它作为*Authorization: Bearer ...*头值,其中你需要用在 Access Base64 Token 字段中找到的字符串替换...。

第九章

  1. MicroProfile Reactive Messaging 是通过连接器处理消息源的一个很好的选择,特别是在消息源以高频率生成消息且异步处理这些消息最合适的情况。

  2. MicroProfile Context Propagation 最佳支持 MicroProfile Reactive Streams Operators 和 MicroProfile Reactive Messaging,因为它允许将传统与当前线程相关联的上下文 propagate across 各种工作单元。

  3. 当前支持反应式编程的规范包括 MicroProfile Reactive Streams Operators、MicroProfile Reactive Messaging 和 MicroProfile Context Propagation。

  4. 在撰写本书时,那些位于...的项目...

第十章

  1. Eclipse MicroProfile 为使用企业级 Java 开发微服务提供了最佳方法之一。反过来,使用容器作为部署单元的微服务为在云和本地部署的高度分布式系统开发提供了最佳方法,即云原生应用。因此,基于 MicroProfile 的微服务有助于云原生应用的开发。

  2. 云原生应用开发有两个互补的方面或组件:应用服务和基础设施服务。应用服务加快了云原生应用的业务逻辑开发,基础设施服务加快了其交付和部署。这两个方面是互补的,是云原生应用开发的重要组成部分。

  3. 云原生应用开发是一种构建和运行应用程序的方法,它充分利用了基于四个关键信条的云计算模型:a) 基于服务的架构(微服务、微小服务、SOA 服务等等);b) 用于服务间通信的 API 驱动方法;c) 基于容器的底层基础设施;d) DevOps 流程。 架构和通信方面与云原生应用的开发关切相关,基础设施和流程方面与它们的交付/部署相关。 Eclipse MicroProfile 与这些信条相关,因为它支持实现可以使用容器作为其底层基础设施的架构的微服务,其中微服务使用它们的 API 进行相互通信,并且使用 DevOps 流程进行开发。

  4. 以下是 Eclipse MicroProfile 如何贡献于指导你向云原生应用迈进的每个八步:

    1. 发展 DevOps 文化和实践:“利用新技术、更快的方法和更紧密的协作,采纳 DevOps 的原则和文化遗产,并围绕这些价值观组织你的组织。” 虽然这是一个与组织和工作流程有关的步骤,但基于微服务的 Eclipse MicroProfile 规范可以很好地适应这种文化和流程的改变,因为微服务由于其特性,紧密支持 DevOps 流程。

    2. 使用快速微服务加速现有应用:“通过迁移到现代、基于容器的平台来加速现有应用的开发——并将单体应用拆分为微服务或微小服务以获得额外的效率提升。” 当您将单体应用拆分为微服务时,Eclipse MicroProfile 能提供很大帮助。 随着您在单体应用中识别出边界上下文,考虑使用 Eclipse MicroProfile 为每个实现边界上下文逻辑的微服务进行实现。

    1. 使用应用服务加速开发:“通过复用性加速软件开发。云原生应用服务是即用的开发者工具。然而,为了最大化收益,这些可复用的组件必须优化并集成到 underlying 云原生基础设施中。”内存中数据网格(IMDG)和消息代理是帮助加速业务逻辑开发的应用服务。使用 Eclipse MicroProfile 开发的微服务可以通过在其方法体中调用这些应用服务来利用它们。当集成到应用服务,如 IMDG 或消息代理时,Eclipse MicroProfile 不会施加任何限制。

    2. 选择合适的工具完成合适的任务:“使用支持正确混合的框架、语言和架构的基于容器的应用平台——并且可以根据您的特定业务应用需求进行定制。”当开发者选择合适的工具来完成合适的任务时,Eclipse MicroProfile 就是这些工具之一。例如,Red Hat Application Runtimes 是一组运行时和工具的集合,其中包括 Eclipse MicroProfile、Node.js、Spring Boot 和 Vert.x。

    3. 为开发者提供自助式按需的基础设施服务:“利用容器和容器编排技术简化对底层基础设施的访问,赋予 IT 运维团队控制力和可见性,并在数据中心、私有云和公有云等各种基础设施环境中提供健壮的应用生命周期管理。”您用 Eclipse MicroProfile 开发的微服务可以部署到一个或多个容器中。通过轻松管理这些容器以及在其上运行的微服务架构,您可以加快开发周期,更快地向企业交付价值。

    4. 自动化 IT 以加速应用交付:“创建用于学习自动化语言和流程的自动化沙盒,建立跨组织协作对话以定义服务需求,创建赋予用户权力并加快交付的自助服务目录,以及使用计量、监控和计费回策略和流程。”Eclipse MicroProfile 提供了度量、容错和健康检查等功能,所有这些都可以作为 IT 自动化流程的输入。

    1. 实现持续交付和先进的部署技术:“利用自动化交付、持续集成/持续交付(CI/CD)流水线、滚动蓝绿部署和金丝雀部署、以及 A/B 测试,加速您的云原生应用的交付。”微服务与 CI/CD 的结合可以促进先进的部署技术。例如,您可以作为蓝绿部署或金丝雀部署的一部分,将具有新功能的基于 MicroProfile 的微服务引入生产环境,一旦验证新功能按预期工作,即可将所有流量切换到它。

    2. 发展更加模块化的架构:“选择一种适合您特定需求的模块化设计,使用微服务、单体优先方法或 miniservices—或它们的组合。”在这个步骤中,您可以使用 Eclipse MicroProfile 为新的应用程序开发微服务,或者将您的单体的特定有限上下文拆分为微服务。

  1. 十二因子应用是一种方法,开发者在设计和实现微服务和应用程序时可以遵循,而不受实现它们的编程语言或框架的限制。开发者可以使用 Eclipse MicroProfile 实现微服务的十二因子应用框架。十二因子应用和 Eclipse MicroProfile 不是相互排斥的,而是相互补充的。

  2. 大多数,如果不是全部,市场 FaaS 提供支持 Java。因此,开发人员可以编写函数体,使用 Eclipse MicroProfile 的许多实现中的一种,这些实现都是用 Java 编写的。Eclipse MicroProfile 的易用性和丰富功能与 FaaS 平台的简单性相结合,可以大大提高开发人员向企业交付价值的速度。此外,实现 Eclipse MicroProfile 的 Quarkus 等技术,占用内存少,启动时间快,是 FaaS 的理想运行时。

  3. 在使用 Eclipse MicroProfile 进行跨云部署时,您需要考虑以下事项:

    • 需要在云环境提供的基础上,配置云环境与本地环境之间的通信路由。

    • 配置 MicroProfile OpenTracing 以启用跨云环境的跟踪捕获。

    • 监控跨云环境的 MicroProfile Metrics 信息拆分

    • 设置 CI 任务以针对适当的云环境,以维护正确的微服务。

  1. 在多云环境中的分布式追踪可能会遇到挑战。我们希望实现与单云环境相同的目标,即可视化一个请求在穿过每个云中的服务以及跨云时所关联的单一路径,但处理不同的上下文传播格式和每个云中追踪数据的存储格式时可能会遇到复杂问题。第一个挑战是要确保追踪可以在不同的云环境中持续进行。这是一个问题,因为截至目前,还没有广泛采用或标准的追踪上下文格式。通常,每个追踪系统使用不同的头和格式来传播追踪上下文。第二个挑战,即使在同构追踪环境中,也要可视化来自多云环境的追踪数据。这可能是个问题,因为每个云中的追踪数据可能存储在不同的数据库中或以不同的格式存储。这可以通过将数据复制到单一统一存储中或根据需要发送缺失的追踪数据并在适当的数据格式调整后,在系统之间发送来克服。

  2. 服务网格,如 Istio 或 LinkerD,在 Kubernetes 之上提供平台级别的服务,涵盖发现、路由和故障容限等领域。其中一些服务也可以在 MicroProfile 中找到。当你将 MicroProfile 应用程序部署到这样的服务网格中时,你需要考虑是否想使用 MicroProfile 中的版本还是网格中的版本。在此受影响最大的 MicroProfile 功能是故障容限,尤其是重试逻辑。

posted @ 2024-05-24 10:54  绝不原创的飞龙  阅读(32)  评论(0编辑  收藏  举报