微服务架构设计模式

微服务架构设计模式

目录

image-20230618152258563.png

image-20230618152332279

image-20230618152350971

image-20230618152406060

image-20230618152418601

第一章 逃离单体地狱

带你读《微服务架构设计模式》之一:逃离单体地狱-阿里云开发者社区 (aliyun.com)

1.6.3 微服务架构的模式语言概述

微服务架构的模式语言是一组模式,可帮助架构师使用微服务架构构建应用程序。
图1-10显示了模式语言的结构。模式语言首先帮助架构师决定是否使用微服务架构。它描述了单体架构和微服务架构,以及它们的好处和弊端。然后,如果微服务架构非常适合当前的应用程序,那么模式语言可以帮助架构师通过解决各种架构和设计问题来有效地使用它。

image.png

这套模式语言由若干组模式构成。在图1-10的左侧是应用程序架构模式组,包括单体架构模式和微服务架构模式。这些模式我们在本章中都已经有所讨论。其余的模式语言包括了一组如何解决采用微服务架构后引入的新问题的模式。
这些模式被分为三组:

  • 基础设施相关模式组:这些模式解决通常是在开发环节跟基础设施有关的问题。
  • 应用基础设施相关模式组:这些模式解决应用层面的基础设施相关问题。
  • 应用相关模式组:这些模式解决开发人员面对的具体技术和架构问题。

这些模式根据所解决问题的不同可进行更进一步的分组。我们先看看其中主要的几组模式。
服务拆分的相关模式
决定如何把系统分解为一组服务,这项工作从本质上来讲是一门艺术,但是即使这样,我们仍旧有一些策略可以遵循。我在服务拆分相关的模式中提出了一些策略,可以用于定义应用程序的架构。如图1-11所示。

image.png

我会在第2章着重介绍这些模式。

通信的相关模式

使用微服务架构构建的应用程序是分布式系统。因此,进程间通信(IPC)是微服务架构的重要组成部分。架构师必须就服务彼此之间以及与外部世界进行通信做出各种架构和设计决策。图1-12显示了通信模式,它们分为以下5组:

  • 通信风格:使用哪一类进程间通信机制?

  • 服务发现:客户端如何获得服务具体实例(如HTTP请求)的IP地址?

  • 可靠性:在服务不可用的情况下,如何确保服务之间的可靠通信?

  • 事务性消息:如何将消息发送、事件发布这样的动作与更新业务数据的数据库事务集成?

  • 外部API:应用程序的客户端如何与服务进行通信?

我会在第3章介绍前4组模式:通信风格、服务发现、可靠性和事务性消息,在第8章讨论外部API模式。

事务一致性 模式

实现事务管理的数据一致性相关模式
如之前提到的,为了确保松耦合,每个服务都必须拥有它自己的数据库。不幸的是,每个服务都有独立的数据库会引入一些大麻烦。例如,我会在第4章中解释为什么我们常用的两步式提交(two phase commit,2PC)分布式事务机制在微服务架构之类场景下就不再适用。取而代之,应用程序需要使用Saga模式来确保数据的一致性。图1-13展示了数据一致性有关的模式。

image.png

image.png

我会在第4~6章中详细讨论这些模式。

查询数据模式

(CQRS / API组合)

在微服务架构中查询数据的相关模式
服务和数据库一一对应还会带来另外一个挑战:有些查询需要从多个服务的数据源获取数据(传统应用采用SQL JOIN的方式完成)。服务的数据仅可以通过API的方式访问,所以我们不能直接针对服务的数据库执行分布式查询。图1-14展示了跟实现查询有关的一些模式。

image.png

有些时候我们可以使用API组合模式,逐一调用服务的API然后把所有的返回聚合在一起。多数情况之下,你需要使用称之为命令查询职责隔离(CQRS)的方式,来维护一些重要和常用的查询数据视图。

在第7章,我会深入探讨实现这些查询的方法。

服务部署模式

高度自动化部署的基础设施。理想情况下,你需要有一个部署平台,包括一个简单的界面(命令行或者图形用户界面都可以)来部署和管理这些服务。这些部署平台往往都是基于虚拟机、容器或者Serverless技术的。我在第12章会详细介绍这些部署方式的差异。

可观测模式

模式可用来设计具备可观测性的服务:

  • 健康检查API:可以返回服务健康状态的API。

- 日志聚合:把服务产生的日志写入一个集中式的日志服务器,这个服务器可以提供日志搜索,也可以根据日志情况触发报警。

  • 分布式追踪:为每一个外部请求分配一个唯一的ID,用于在各个服务之间追踪外部请求。
  • 异常跟踪:把程序异常发送到异常跟踪服务,这个服务会排除重复异常,给开发者发送告警并且跟踪每一个异常的解决。
  • 应用指标:供维护使用的指标,例如计数器等,导出到指标服务器。
  • 审计日志:记录用户的行为。

image.png

我会在第11章深入讨论可观测模式。

自动化测试模式

实现服务自动化测试的相关模式
微服务架构让单一的服务测试变得容易,因为相比单体应用,每一个服务都变得更小了。但与此同时,重要的是测试不同的服务是否协同工作,同时避免使用复杂、缓慢和脆弱的端到端测试来测试多个服务。以下是通过单独测试服务来简化测试的模式:

  • 消费端驱动的契约测试:验证服务满足客户端所期望的功能。
  • 消费端契约测试:验证服务的客户端可以正常与服务通信。
  • 服务组件测试:在隔离的环境中测试服务

在第9章和第10章中,我们会介绍这些跟测试有关的模式。

安全相关的模式

在微服务架构中,用户身份验证的工作通常由API Gateway完成。然后,它必须将有关用户的信息(例如身份和角色)传递给它调用的服务。常见的解决方案是应用访问令牌模式。API Gateway将访问令牌(例如JWT,即JSON Web令牌)传递给服务,这些服务可以验证令牌并获取有关用户的信息。第11章将更详细地讨论访问令牌模式。

本章小结

  • 单体架构模式将应用程序构建为单个可部署单元。
  • 微服务架构模式将系统分解为一组可独立部署的服务,每个服务都有自己的数据库。
  • 单体架构是简单应用的不错选择,微服务架构通常是大型复杂应用的更好选择。
  • 微服务架构使小型自治团队能够并行工作,从而加快软件开发的速度。
  • 微服务架构不是银弹:它存在包括复杂性在内的诸多弊端。
  • 微服务架构模式语言是一组模式,可帮助你使用微服务架构构建应用程序。它可以帮助你决定是否使用微服务架构,如果你选择微服务架构,模式语言可以帮助你有效地应用它。
  • 你需要的不仅仅是通过微服务架构来加速软件交付。成功的软件开发还需要DevOps和小而自治的团队。
  • 不要忘记采纳微服务过程中的人性层面。你需要考虑员工的情绪才能成功转换到微服务架构。

第二章 服务的拆分策略

带你读《微服务架构设计模式》之二:服务的拆分策略-阿里云开发者社区 (aliyun.com)

什么是微服务架构

—软件架构 4 + 1

image.png

每个视图的目的如下:

  • 逻辑视图:开发人员创建的软件元素。在面向对象的语言中,这些元素是类和包。它们之间的关系是类和包之间的关系,包括继承、关联和依赖。
  • 实现视图:构建编译系统的输出。此视图由表示打包代码的模块和组件组成,组件是由一个或多个模块组成的可执行或可部署单元。在Java中,模块是JAR文件,组件通常是WAR文件或可执行JAR文件。它们之间的关系包括模块之间的依赖关系以及组件和模块之间的组合关系。
  • 进程视图:运行时的组件。每个元素都是一个进程,进程之间的关系代表进程间通信。
  • 部署视图:进程如何映射到机器。此视图中的元素由(物理或虚拟)计算机和进程组成。机器之间的关系代表网络。该视图还描述了进程和机器之间的关系。

除了这四个视图以外,4+1中的+1是指场景,它负责把视图串联在一起。每个场景负责描述在一个视图中的多个架构元素如何协作,以完成一个请求。例如,在逻辑视图中的场景,展现了类是如何协作的。同样,在进程视图中的场景,展现了进程是如何协作的。
4+1视图是描述应用程序架构的绝佳方式。每一个视图都描述了架构的一个重要侧面。场景把视图中的元素如何协作串联在一起。现在我们来看看为什么架构是如此重要。

为什么架构如此重要
应用程序有两个层面的需求。第一类是功能性需求,这些需求决定一个应用程序做什么。这些通常都包含在用例(use case)或者用户故事(user story)中。应用的架构其实跟这些功能性需求没什么关系。功能性需求可以通过任意的架构来实现,甚至是非常糟糕的大泥球架构。
架构的重要性在于,它帮助应用程序满足了第二类需求:非功能性需求。我们把这类需求也称之为质量属性需求,或者简称为“能力”。这些非功能性需求决定一个应用程序在运行时的质量,比如可扩展性和可靠性。它们也决定了开发阶段的质量,包括可维护性、可测试性、可扩展性和可部署性。为应用程序所选择的架构将决定这些质量属性。

—分层架构

它将应用程序的类组织到以下层中:

  • • 表现层:包含实现用户界面或外部 API 的代码。
  • • 业务逻辑 层:包含业务逻辑。
  • • 数据 持久化层: 实现与数据库交互的逻辑。

分层架构是架构风格的一个很好的例子,但它确实有一些明显的弊端: ,

  • 单个表现层:它无法展现应用程序可能不仅仅由单个系统调用的事实。
  • • 单一 数据持久化层:它无法展现应用程序可能与多个数据库进行交互的事实。
  • • 将业务逻辑层定义为依赖于数据持久化层:理论上,这样的依赖性会妨碍你在没有数 据库的情况下测试业务逻辑

—六边形架构

六边形架构是分层架构风格的替代品。如图2-2所示,六边形架构风格选择以业务逻辑 为中心的方式组织逻辑视图。应用程序具有一个或多个人站适配器,而不是表示层,它通过 调用业务逻辑来处理來自外部的请求。同样,应用程序具有一个或多个出站适配器,而不是数据持久化层,这些出站适配器由业务逻辑调用并调用外部应用程序。此架构的一个关键特 性和优点是业务逻辑不依赖于适配器。相反,各种适配器都依赖业务逻辑。

image-20230701164302888

模式:单体架构 将应用程序构建为单个可执行和可部署组件。

模式:微服务架构
将 应 用 程 序 构 建 为 松 耜 合 、 可 独 立部 署 的 一 组 服 务

为应用程序定义微服务架构

—根据业务能力进行服务拆分

模式:根据业务能力进行服务拆分
定 义 与业 务 能 力 相 对应 的 服 务。

image-20230701164916552

—根 据子域进行服务拆分

模式 : 根据 子 域 进 行 服 务 拆 分 根据DDD的子域设计服务

image-20230701164904368

—拆分的指导原则

  • 第 一个原则就是在定义类的职责时,应该遵循单一职责原则(SingleResponsibilityPrinciple, SRP)。
  • 第 二个原则是把类组成包时,应该遵循闭包原则(Common ClosurePrinciple, CCP)

单一职责 原则
软件架构和设计的主要目标之一是确定每个软件元素的职责。单一职责原则如下:
改变一 个类应 该只 有一 个理 由

闭 包原 则
另外一个有用的原则是闭包原则(CCP):
在包 中 包 含 的 所 有 类应 该 是 对 同类 的变 化 的 一 个 集 合, 也就 是 说, 如 果 对包 做 出修 改, 需要 调整 的类应 该都在这 个 包之 内。

—拆分难点

你可能会遇到几个障碍:

  • • 网络延迟。
  • 同步进程间通信导致可用性降低。
  • • 在服务之间维持数据 一致性。
  • • 获取一致的数据视图。
  • • 上帝类阻碍了拆分。

第三章 微服务架构中的进程通信

微服务架构中的进程间通信概述

—交互方式

  • 第 一个维度 关注的是一对一和一对多。
    • • 一对一:每个客户端请求由一个服务实例来处理。
    • • 一对多:每个客户端请求由多个服务实例来处理。
  • 交互方式的第 二个维度关注的是同步和异步。
    • • 同步模式:客户端请求需要服务端实时响应,客户端等待响应时可能导致堵塞。
    • • 异步模式:客户端请求不会阻塞进程,服务端的响应可以是非实时的。

image-20230701165349657

—在微服务架构中定义API

1、API 优先设计

2、语 义化版 本控 制
语义化版本控制规范(http://semver.org)为API 版本控制提供了有用的指导。它是一组 规则,用于指定如何使用版本号,并且以正确的方式递增版本号。语义化版本控制最初的目 的是用软件包的版本控制,但你可以將其用在分布式系统中对API 进行版本控制。

语义化版本控制规范(Semvers)要求版本号由三部分组成:MAJOR. MINOR. PATCHo 必须按如下方式递增版本号:

  • • MAJOR:当你对API 进行不兼容的更改时。
  • • MINOR:当你对API 进行向后兼容的增强时。
  • • PATCH:当你进行向后兼容的错误修复时。

—消息格式

基于同步远程过程调用模式的通信

—RESTful

REST的好处和弊端
REST有如下好处:

  • 它非常简单,并且大家都很熟悉。
  • 可以使用浏览器扩展(比如Postman插件)或者curl之类的命令行(假设使用的是JSON或其他文本格式)来测试HTTP API。
  • 直接支持请求/响应方式的通信。
  • HTTP对防火墙友好。
  • 不需要中间代理,简化了系统架构。

它也存在一些弊端:

  • 它只支持请求/响应方式的通信。
  • 可能导致可用性降低。由于客户端和服务直接通信而没有代理来缓冲消息,因此它们必须在REST API调用期间都保持在线。
  • 客户端必须知道服务实例的位置(URL)。如3.2.4节所述,这是现代应用程序中的一个重要问题。客户端必须使用所谓的服务发现机制来定位服务实例。
  • 在单个请求中获取多个资源具有挑战性。
  • 有时很难将多个更新操作映射到HTTP动词。

—gRPC

gRPC有几个好处:

  • 设计具有复杂更新操作的API非常简单。
  • 它具有高效、紧凑的进程间通信机制,尤其是在交换大量消息时。
  • 支持在远程过程调用和消息传递过程中使用双向流式消息方式。
  • 它实现了客户端和用各种语言编写的服务端之间的互操作性。

gRPC也有几个弊端:

  • 与基于REST/JSON的API机制相比,JavaScript客户端使用基于gRPC的API需要做更多的工作。
  • 旧式防火墙可能不支持HTTP/2。

gRPC是REST的一个引人注目的替代品,但与REST一样,它是一种同步通信机制,因此它也存在局部故障的问题。让我们来看看它是什么以及如何处理它。

—使用断路器模式处理局部故障

模式:断路器
这是一个远程过程调用的代理,在连续失败次数超过指定阅值后的一段时间内,这 个 代 理 会 立即 拒 绝 其 他 调 用

发 可 靠 的 远 程 过 程 调用 代 理:

  • 网络超时:在等待针对请求的响应时,一定不要做成无限阻塞,而是要设定一个超 时。使用超时可以保证不会一直在无响应的请求上浪费资源。
  • • 限制容户端向 服务器发出请求的数量:把客户端能够向特定服务发起的请求设置一个 上限,如果请求达到了这样的上限,很有可能发起更多的请求也无济于事,这时就应 该让请求立刻失败
  • • 断路器模式:监控客户端发出请求的成功和失败数量,如果失败的比例超过一定的阈 值,就启动断路器,让后续的调用立刻失效。如果大量的请求都以失败而告终,这说 明被调服务不可用,这样即使发起更多的调用也是无济于事。在经过一定的时间后, 客户端应该继绞尝试,如果调用成功,则解除断路器。

—使用服务发现

基于异步消息模式的通信

使用异步消息提高可用性

- 第四章 - 使用Sage管理事务

微服务架构事务管理

—XA

分布式事务管理的事实标准是X/OpenDistributed Transaction Procesing(DTP)Model。

采用两阶段提交

—Sage

模 式: S a g a 通过使用异步消息来协调一系列本地事务,从而维护多个服务之同的数据一致性。

本地事务补偿机制 : 可补偿事务、关键性事务、可重复性事务

服务架构设 计模式 Sa g a 的 结构
上一节中提到的对策论文定义了一个有用的Saga 结构模型。在如图4-8所示的模型中, 一个 S a g a 包 含 三种 类 型 的 事 务 。

  • 可补偿性事务:可以使用补偿事务回滚的事务。
  • 关键性事务:Saga 执行过程的关键点。如果关键性事务成功,则Saga 将一直运行到 完成。关键性事务不见得是 一个可补偿性事务,或者可重复性事务。但是它可以是最 后一个可补偿的事务或第一个可重复的事务。
  • 可重复性事务:在关键性事务之后的事务,保证成功。

协调逻辑

Saga 的实现包含协调Saga 步骤的逻辑。当通过系统命令启动Saga 时,协调逻辑必须选 择并通知第一个Saga 参与方执行本地事务。

  • 一旦该事务完成,Saga 协调选择并调用下 一个 Saga 参与方。这个过程 一直持续到Saga 执行完所有步骤。
  • 如果任何本地事务失败,则Saga 必须以相反的顺序执行补偿事务。

方案

以下几种不同的方法可用来构建Saga 的协调逻辑。
• 协 同 式 ( c h o r e o g r a p h y ): 把 S a g a 的 决 策 和 执 行 顺 序 逻 辑 分 布 在 S a g a 的 每 一 个 参 与 方 中,它们通过交换事件的方式来进行沟通。(决策 和 执行顺序 逻辑在参与方)
• 编排式(orchestration):把Saga的决策和执行顺序逻辑集中在一个Saga编排器类中。 Saga 编排器发出命令式消息给各个Saga 参与方,指示这些参与方服务完成具体操作 (本地事务)。 (逻辑在 Sage 编排器中)

—协同式

image-20230618223158396

协同式Sa ga的好处和弊端
基于协同式的Saga 有以下几个好处:

  • 简单:服务在创建、更新或删除业务对象时发布事件。
  • 更难理解:与编排式不同,代码中没有一个单一地方定义了Saga 。相反,协调式 Saga 的逻辑分布在每个服务的实现中。因此,开发人员有时很难理解特定的Saga 是 如何 工作的。
    服务之间的循环依赖关系:Saga 参与方订阅彼此的事件 ,这通常会导致循环依 赖 关 系 。 例 如, 如 果 仔 细 检 查 图 4 - 4 , 你 将 看 到 存 在 循 环 依 赖 关 系, 例 如 o r d e r Service -Account ing service -order service。虽然这并不一定是个问
    题,但循环依赖性被认为是 一种不好的设计风格。
  • 松趨合:参与方订阅事件并且彼此之间不会因此而产生赮合。

但它也有以下一些弊端:

  • 更难理解:与编排式不同,代码中没有一个单一地方定义了Saga 。相反,协调式 Saga 的逻辑分布在每个服务的实现中。因此,开发人员有时很难理解特定的Saga 是 如何 工作的。
  • 服务之间的循环依赖关系:Saga 参与方订阅彼此的事件 ,这通常会导致循环依 赖 关 系 。 例 如, 如 果 仔 细 检 查 图 4 - 4 , 你 将 看 到 存 在 循 环 依 赖 关 系, 例 如 o r d e r Service -Account ing service -order service。虽然这并不一定是个问
    题,但循环依赖性被认为是 一种不好的设计风格。
  • 紧趨合的风险:每个Saga参与方都需要订阅所有影响它们的事件。例如,Account ing s e r v i c e 必 须 订 阅 所 有 可 能 导 致 消 费 者 信 用 卡被 扣 款 或 退 款 的 事件 。 因 此, 存 在一 种风险,即Accounting service的内部代码需要与order service实现的订
    单生命周期代码保持同步更新

—编排式

image-20230618223102083

编排式 S a g a 的 好 处和弊 端 基于编排的Saga 有以下好处:
• 更 简 单 的 依 赖 关 系 : 编 排 的 一个 好 处 是 它 不 会 引 人 循 环 依 赖 关 系 。 S a g a 编 排 器 调 用 Saga 参与方,但参与方不会调用编排器。因此,编排器依赖于参与方,但反之则不 然,因此没有循环依赖。
• 较少的耜合:每个服务实现供编排器调用的API,因此它不需要知道Saga 参与方发 布的事件。
• 改善关注点隔离,简化 业务逻辑:Saga 的协调逻辑本地化在Saga 编排器中。领域对 象更简单,并且不需要了解它们参与的Saga。例如,当使用编排式Saga时,or der 类不知道任何Saga,因此它具有更简单的状态机模型。在执行Create or der saga 期间,它直接从APPROVAL_PENDING状态转换到APPROVED状态。order 类没有与Saga 的步骤相对应的任何中间状态。因此,业务更加简单。

编排式Saga 也有 一个弊端: 在编排器中存在集中过多业务逻辑的风险

解决Sage的隔离问题

Sage失去 I 隔离性:

  • 因为一旦该事务提交,每 个Saga 的本地事务所做的更新都会立即被其他Sagas 看到。此行为可能导致两个问题。
  • 首先,其他Sa ga 可以在执行时更改该Sa ga 所访问的数据。
  • 其他Saga 可以在Saga 完成更新之 前读取其数据,因此可能会暴露不一致的数据。

你可以认为Saga 只满足ACD 三个特性:

  • • 原子性:Saga 实现确保执行所有事务或撤销所有更改。
  • 一致性:服务内的参照完整性 (referential integrity)由本地数据库处理。服务之间的参照完整性由服务处理。
  • • 持久性:由本地数据库处理。

—隔离导致异常问题

缺乏隔离可能导致以下 三种异常:
• 丢失更新:一个Saga没有读取更新,而是直接覆盖了另一个Saga所做的更改。
• 脏读:一个事务或一个Saga读取了尚末完成的Saga所做的更新。
• 模糊或不可重复读:一个Saga的两个不同步骤读取相同的数据却获得了不同的结果, 因 为 另 一个 S a g a 已 经 进 行 了 更 新 。

— Sage的对策

  • • 语义锁:应用程序级的锁。
  • • 交换式更新:把更新操作设计成可以按任何顺序执行。
  • • 悲观视图:重新排序Saga的步骤,以最大限度地降低业务风险。
  • • 重读值:通过重写数据来防止脏写,以在覆盖数据之前验证它是否保持不变。
  • • 版本文件:将更新记录下来,以便可以对它们重新排序。
  • 。业务风险评级( by val ue):使用每个请求的业务风险来动态选择并发机制。

编写Sage

Orderservice创建和更新order ,调 用 o r d e r Re p o s i t o r y 来 持 久 化 o r d e r , 并 使 用 S a g a m a n a g e r 创 建 Sa g a , 例 如 CreateorderSagaoSagaManager类是Eventuate TramSaga框架提供的类之一,Eventuate T r a m S a g a 框 架 是 一个 用 于 编 写 S a g a 编 排 器 和 参 与 方 的 框 架 , 本 节 稍 后 将 对 此 进 行 讨 论 。

— OrdeService类

O r d e r s e r v i c e 类 是 一 个 由 服 务 的 A P I 层 调 用 的 领 域 服 务。 它 负 责 创 建 和 管 理 订 单

—Create Order Saga 的实现

  • • CreateorderSaga:定义Saga状态机的单例类。它调用Createordersagastate 来创建命令式消息,并使用Saga 参与方代理类(例如KitchenserviceProxy)指定 的消息通道将它们发送给参与方。
  • • Createordersagastate:一个Saga的持久状态,用于创建命令式消息。
  • • Saga参与方的代理类,例如KitchenserviceProxy:每个代理类定义一个Saga 参与方的消息API ,它由命令通道、命令式消息类型和回复类型组成。

Eventuate TramSaga框架提供了一种特定于领域的语言(DSL),用于定义Saga 的状态 机。它执行Saga的状态机,并使用Eventuate Tram框架与Saga参与方交换消息。该框架还 将Saga 的状态持久化在数据库中。

C r e a t e Or de r S ag a 编 排器
C r e a t e o r d e r s a g a 类 实 现 了前 面 图 4 - 7 所 示 的 状 态 机 。 这 个 类 实 现 了 S i m p l e s a g a , 它是Saga的基本接又。createordersaga 类的核心是代码清单4-2中显示的Saga定义。 它使用EventuateTramSaga框架提供的DSL来定义create order saga的步骤。

CreateOrderSagaState 类
CreateorderSagastate 类(如代码清单4-4所示)表示Saga实例的状态。此类的 实例由Orderservice创建,并由EventuateTramSaga框架持久化保存在数据库中 。它的
主要职责是创建发送给Saga 参与方的消息。

Ki t c h e n S er vi c e P r o x y 类
代码清单4-5中所示的Kitchenserviceproxy类定义了Kitchen service的命 令式消息端点,这里有3 个端点。
• create:创建Ticket。
• confirmcreate:确认创建。 • cancel:取消Ticket。

EventuateTramSa ga框架
如 图 4 - 1 2 所 示,E v e n t u a t e T r a m S a g a 是 一 个 用 于编 写 S a g a 编 排 器 和 S a g a 参 与 方 的 框 架 。 它使用Eventuate Tram 的事务性消息能力,我们在第了章中曾经讨论过。
S a g a O r c h e s t r a t i o n 包 是 框 架 中 最 复 杂 的 部 分 。 它 提 供 了 S i mp l e s a g a , 这 是 S a g a 的基本接又,以及一个创建和管理Saga实例的SagaManager 类。SagaManager 用于处 理持久化Saga ,发送它生成的命令式消息、订阅回复消息并调用Saga 来处理回复。因4- 13 显 示 了 O r d e r s e r v i c e 创 建 S a g a 时 的 事 件 序 列 。 事 件 顺 序 如 下:

image-20230701164005559

-第五章 微服务架构中的业务逻辑设计

业务逻辑组织模式

图5- 1 显示了一个典型的服务架构,业务逻辑的周圃是人站和出站适配器。

  • 入站适配器处理来自容户端的请求并调用业务逻辑。
  • 出站适配器被业务逻辑调用,然后它们再调用其他服务和外部应用程序。

此服务由业务逻辑和以下适配器组成。
• REST API adapter:人站适配器,实现RESTAPI,这些API会调用业务逻辑。
• OrdercommandHlandlers:人站适配器,它接收来自消息通道的命令式消息,并调 用业务逻辑。
• Database Adapter:由业务逻辑调用以访问数据库的出站适配器。
• Domain Event Publishing Adapter:将事件发布到消息代理的出站适配器。

image-20230629234046571

—事务脚本模式设计业务逻辑

编写一个称为事务脚本的方法来处理来自表示层的每个请求,而不是进行任何面向对象的设 计。

模式:事务脚本

将业秀逻輯组织为面向过程的事务脚本的集合,每种类型的请求都有一个脚本。

image-20230629234323084

这种设计风格是高度面向过程的,仅仅依赖于面向对象编程(OOP)语言的少量功能。 就好比你使用C或其他非OOP 语言编写应用所能实现的功能。然而,在适当的时候,你不 应该羞于使用面向过程的设计。这种方法适用于简单的业务逻辑。但这往往不是实现复杂业 务逻辑的好方法。

—领域模型模式设计业务逻辑

模式:领域模型

将业务逻辚组织为由具有状态和行为的类构成的对象模型。

image-20230629234308466

  • 首先,这样的设计易于理解和维护。它不是由一个 完成所有事情的大类组成,而是由许多小类组成,每个小类都有少量职责。此外,诸如 A c c o u n t 、 B a n k i n g T r a n s a c t i o n 和 o v e r d r a f t P o l i c y 这 些 类 都 密 切 地 反 映 了现 实 世界,这使得它们在设计中的角色更容易理解。

  • 其次,我们的面向对象设计更容易测试:每 个类都可以并且应该能够被独立测试。

  • 最后,面向对象的设计更容易扩展,因为它可以使用 众所周知的设计模式,例如策略模式(Strategy pattern)和模板方法模式( Templatemethod pattern ),这些设计模式定义了在不修改代码的情况下扩展组件的方法。

—领域模型模式

将业务逻辚组织为由具有状态和行为的类构成的对象模型

领域驱动设计(Domain-DrivenDesign, DDD)的概念产生于EricEvans写的《Domain D r i v e n D e s i g n 》 日 一 书, D D D 是 对 面 向 对 象 设 计 的 改 进 , 是 开 发 复 东 业 务 逻 拜 的 一 种 方 法 。
我在第2 章介绍过DDD,领域驱动设计的子域概念有助于把应用程序分解为服务。使用 DDD 时,每个服务都有自己的领域模型,这就避免了在单个应用程序全局范围内的领域模
型问题。 子域和相关联的限界上下文的相关概念是两种战略性DDD 模式。
D D D 还 有 一些 战 术 性 模 式 , 它 们 是 领 域 模 型 的 基 本 元 素 ( b u i l d i n g b l o c k ) 。 每 个 模 式 都 是 一个类在领域模型中扮演的角色,并定义了类的特征。开发人员广泛采用的基本元素包括
以下几种。

  • 实体(entity):具有持久化D的对象。具有相同属性值的两个实体仍然是不同的对 象 。 在 J a v a E E 应 用 程 序 中, 使 用 J P A @ E n t i t y 进 行 持 久 化 的 类 通 常 是 D D D 实 体 。
  • 值对象( value object) :作为值集合的对象。具有相同属性值的两个值对象可以互换
    使用。值对象的一个例子是wone y 类,它由币种和金额组成。
  • 工厂( factory):负责实现对象创建逻辑的对象或方法,该逻辑过于复杂,无法由类的
    构造两数直接完成。它还可以隐藏被实例化的具体类。工厂方法 一般可实现为类的静 态方法。
  • 存储库(repository):用来访问持久化实体的对象,存储库也封装了访问数据库的底 层机制。
  • 服务( service ):实现不属于实体或值对象的业务逻辑的对象。

使用聚合模式设计领域模型

模式:聚合

将领域模型组织为聚合的集合,每个聚合都是可以作为一个单元进行处理的一组对 象构成的图。

image-20230629234610291

—-聚合使用规则

规则一:只引用聚合根

​ 前面的例子说明了直接更新OrderLineItems可能引发业务规则失效问题。第 一个聚 合规则的目标是消除这个问题。它要求聚合根是聚合中唯一可以由外部类引用的部分。客户 端只能通过调用聚合根上的方法来更新聚合。

规 则 二 : 聚合 间 的 引 用 必须 使用 主 键

规则三:在一个事务中, 只能创建或更新一个聚合

这个约束对于微服务架构来说是完美的。它可以确保单 个事务的范围不超越服务的边界。此约束还满足大多数NoSQL数据库的受限事务模型。 这个规则让创建或更新多个聚合的操作变得更加复杂。但这正是Saga (第4 章中描述过) 旨在解决的问题。Saga 的每一步都只创建或更新一个聚合。

image-20230701153205910

—设计领域模型策略

划分实体与聚合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
领域模型设计是指针对特定业务领域进行建模和设计的过程。在领域模型设计中,划分实体类和通过聚合的方式减少服务(service)对实体的依赖是一种常见的做法,但不是唯一的方法。

在领域模型设计中,可以通过以下几个步骤来划分实体类和减少服务对实体的依赖:

1. 领域建模:从实际业务需求中提取概念和业务场景,将其转化为领域模型对象(实体、值对象、聚合等)。这些对象代表了领域中的实体和关系。

2. 划分实体类:根据业务需求和功能划分,将领域模型中的实体划分为不同的类。每个类应该具有明确的职责和相关的行为(方法)。

3. 定义聚合:识别主要的聚合根(Aggregate Root),它是整个聚合的入口点和唯一访问点。通过聚合根来管理其他相关实体和值对象。

4. 减少服务对实体的依赖:服务(service)是处理复杂业务逻辑的组件,而实体则代表领域的核心概念。通过将业务逻辑尽可能地放在实体中,可以减少服务对实体的依赖。这样可以使实体更具职责和封装性,更好地维护和测试。

除了划分实体和聚合,还有其他策略可以帮助设计领域模型,例如:

- 设计DDD(领域驱动设计)的领域服务来处理跨聚合的业务逻辑。
- 使用值对象和不变性来增加模型的表达力和一致性。
- 采用事件驱动的设计模式,以在领域模型之间实现解耦和松散耦合。

需要根据具体的业务需求和上下文,选择适合的领域模型设计和架构模式。领域模型设计旨在实现业务需求的可理解性、可维护性和灵活性,并使开发和维护过程更加简化和高效。

—-其他策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
除了划分实体和聚合以及减少服务(service)对实体的依赖之外,设计领域模型还可以使用以下策略:

1. 领域驱动设计(DDD):DDD是一种软件开发方法,重点在于深入理解业务领域,并将这些理解直接反映在领域模型中。通过DDD,可以通过建立明确的职责边界、使用领域服务、值对象和聚合等方式来设计具有高内聚性和低耦合度的领域模型。

2. 值对象:值对象(Value Object)是指那些没有唯一标识且仅由其属性值定义的对象。值对象在领域模型中用于表示不可变的概念,并且通常被用作实体的属性。值对象有助于增加模型的表达力和一致性,同时减少重复代码。

3. 领域服务:领域服务(Domain Service)是处理领域模型中复杂业务逻辑的操作。它们不属于任何具体的实体或聚合,而是独立于特定实体的概念。领域服务可以跨越多个聚合,处理复杂的业务操作。

4. 仓储模式:仓储(Repository)是一种用来管理聚合之间的持久化和检索操作的模式。仓储提供了一种将领域对象(实体和聚合)持久化到存储介质(如数据库)的抽象接口。通过仓储模式,能够将数据访问逻辑与领域模型的逻辑分离,并提供方便的数据访问接口。

5. 事件驱动设计:事件驱动设计(Event-Driven Design)将系统的交互和通信基于事件和消息的流动。领域模型设计可以使用事件来表示系统中发生的重要事情。通过使用事件,可以实现模块和组件之间的解耦和松散耦合,同时支持可扩展性和可维护性。

6. 聚合根:聚合根(Aggregate Root)是聚合中的根实体,他负责管理和保护聚合内的其他实体和领域对象。聚合根通过封装子实体和值对象,确保聚合中的所有对象都满足一致性和业务规则。

这些策略可以根据具体的业务需求来选择和应用。重要的是,设计领域模型时要保持简洁、高内聚、低耦合,并能够准确反映业务领域的概念和规则。采用合适的策略和模式有助于实现可维护、可扩展和易于理解的领域模型。

—使用示例

以下是关于领域模型设计策略的一些示例:

  1. 领域驱动设计(DDD):例如,在电子商务领域中,可以通过使用DDD来设计订单管理系统。可以将订单作为聚合根,并在订单中定义包含商品、收货地址、支付等相关的实体和值对象。通过使用DDD的聚合、领域服务和限界上下文的概念,可以更好地表示订单领域的复杂业务逻辑。(Domain — 实体)

  2. 值对象:例如,考虑一个电影订票系统中的座位预订功能。可以使用值对象来表示座位,其中座位可以是由行号和列号组成的不可变对象。这样可以减少对座位的直接修改和引用,提高模型的一致性和可维护性。(k-v)

  3. 领域服务:例如,在酒店预订系统中,可以设计一个预订服务来处理预订的复杂逻辑。该服务可以与酒店、房间和客户等实体进行交互,执行诸如创建预订、检查可用性、计算价格等操作。(service。– 服务)

  4. 仓储模式:例如,在一个论坛应用中,可以使用仓储模式来管理帖子和用户的持久化。创建一个帖子或用户的仓储接口,并在具体的实现中将其持久化到数据库。这样可以实现数据访问与领域模型的分离,提高系统的可测试性和可替换性。(repository – 持久层)

  5. 事件驱动设计:例如,在一个物流系统中,可以使用事件来处理订单状态的变更。当订单状态发生改变时,可以发布一个订单状态变更的事件,然后订阅该事件的系统组件将根据事件触发相应的操作,如发送通知、更新库存等。这种事件驱动的设计方式能够实现松散耦合和灵活性。(事件发布/订阅 模型)

  6. 聚合根:例如,在一个社交媒体应用中,可以将用户的个人信息、好友关系和发布的文章作为一个聚合根。聚合根可以封装子实体和值对象,并定义一致性和业务规则。通过聚合根,可以保证整个领域模型的完整性和一致性。(实体聚合 – Data Transfer Object)

这些示例仅用于说明设计策略的使用情况,实际应用中还需要根据具体的业务需求和问题来做出设计决策。关键是理解并应用合适的策略,以确保领域模型的可理解性、可维护性和可扩展性。

—代码

当涉及到领域模型的具体实现代码时,很难提供一个通用的示例,因为每个业务领域和场景都有不同的要求。以下是一个简单的示例,演示了如何在Java中使用领域模型设计策略:

  1. 值对象示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Seat {
private final int row;
private final int column;

public Seat(int row, int column) {
this.row = row;
this.column = column;
}

// Getter methods

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Seat seat = (Seat) o;
return row == seat.row && column == seat.column;
}

@Override
public int hashCode() {
return Objects.hash(row, column);
}

@Override
public String toString() {
return "Seat{" +
"row=" + row +
", column=" + column +
'}';
}
}
  1. 聚合根和实体示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Order {
private final String orderId;
private final List<OrderItem> items;

public Order(String orderId) {
this.orderId = orderId;
this.items = new ArrayList<>();
}

public void addItem(OrderItem item) {
this.items.add(item);
}

public void removeItem(OrderItem item) {
this.items.remove(item);
}

// Getter methods

@Override
public String toString() {
return "Order{" +
"orderId='" + orderId + '\'' +
", items=" + items +
'}';
}
}

public class OrderItem {
private final String productId;
private final int quantity;

public OrderItem(String productId, int quantity) {
this.productId = productId;
this.quantity = quantity;
}

// Getter methods

@Override
public String toString() {
return "OrderItem{" +
"productId='" + productId + '\'' +
", quantity=" + quantity +
'}';
}
}
  1. 领域服务示例:
1
2
3
4
5
6
7
public class ReservationService {
public boolean reserveSeats(List<Seat> seats) {
// Perform reservation logic
// ...
return true; // or false based on success/failure
}
}

这些示例仅用于说明如何使用领域模型设计策略,更复杂的实现将会涉及更多的业务规则和场景。在实际的应用程序中,您需要根据具体的业务需求和架构来实现领域模型,以及将其与业务逻辑和数据访问层进行集成。

发布领域事件

模式:领域事件

聚合在被创建时,或发生其他重大更政时发布领域事件。

第六章 事件溯源(消息通知)

image-20230620094343113.png

概述

事件溯源(Event Sourcing)是一种软件设计模式,用于捕获和存储应用程序中发生的所有事件,并以事件为基础来重建应用程序的状态。它将应用程序状态的变化建模为一系列不可变的事件,这些事件按顺序存储,可以用于重演和还原应用程序的状态。

事件溯源的核心思想是将应用程序的状态更改视为事件流,每个状态更改都会生成一个事件,并将其持久化到一个事件日志中。通过对这些事件的回放,应用程序可以重建完整的状态历史,从而实现时间旅行的能力。

以下是事件溯源的主要特点:

  1. 完整性和可追溯性:通过存储每个事件,应用程序的状态更改得以完整记录和追溯。每个事件都包含足够的信息来精确重建状态。

  2. 不可变性:事件一旦生成就不可更改,这使得所有状态更改都可以被严格追溯,并提供了安全审计和数据完整性的好处。

  3. 时间驱动的重建:通过按顺序回放事件,可以重新构建每个状态更改的历史,以及再现任何特定时间点的应用程序状态。

  4. 多模型支持:由于事件日志包含所有状态更改的详细信息,可以基于这些事件构建多个视图或模型,以满足不同的查询和分析需求。

事件溯源的优势包括:

  • 审计和合规性:事件日志记录了所有状态更改,可以用于审计和合规性目的。每个事件都包含了发生更改的时间、操作人员等关键信息。

  • 恢复和故障转移:通过回放事件日志,可以恢复应用程序到任何时间点的状态,且可以简化故障转移和灾难恢复。

  • 历史数据分析:事件溯源提供了完整的历史状态变化,可以用于分析和生成报表,以了解系统的行为和性能。

尽管事件溯源可以提供诸多好处,但也需要考虑其对存储和性能的要求。事件日志可能会变得极其庞大,因此在设计和实施中需要权衡存储成本和性能需求。此外,由于事件溯源涉及到应用程序的整个状态历史记录,因此在设计和维护时需要仔细考虑和规划。

书-概念

事件湖源是构建业务逻辑和持久化聚合的另 一种选择。它将聚合以 一系列事件的方式持 久化保存。每个事件代表聚合的一次状态变化。应用程序通过重放(replaying)事件来重新创建聚合的当前状态。

模式:事件溯源
使用一系列表示状态更政的领城事件来持久化聚合。

第七章 查询实现

API组合

参与者

两种类型的参与者:

  • • API 组合器:它通过查询数据提供方的服务来实现查询操作。
  • • 数 据 提供 方服 务: 拥 有 查 询 返 回 的 部 分 数 据的 服 务

缺陷

使用此模式时,你必须解决两个设计问题:

  • • 确定架构中的哪个组件是查询操作的API组合器。
  • • 如何编写有效的聚合逻辑。

由谁来担任API组合器的角色?
这是你必领做出的一个决定,选择由谁来扮演查询操作的API 组合器这个角色。你有 三 个 选 择 。

第 一个 选 择 , 如 图 7 - 4 所 示 , 是 由 服 务 的 容 户 端 扮 演 A P I 组 合 器 的 角 色 。

第 二 个 选 择 ( 如 图 7 - 5 所 示 ), 由 实 现 应 用 程 序 外 部 A P I 的 A P I G a t e w a y 来 扮 演 A P I 组 合 器的角色,用来完成查询操作和查询结果的组合

第三个选择(如图7-6 所示)是将 API 组合器实现为独立的服务。

API 组合器应该使用响应式编程模型

在开发分布式系统时,我们一直努力降低服务之间的延退。API组合 器应尽可能地并行 调用提供方服务,最大限度地缩短查询操作的响应时间。

CQRS

模式:命令查询职责隔离 ( CQRS )

使用事件来维护从多个服务复制数据的只读视图,借此实现对来自多个服务的数据 的查询。请参阅:http://microservices.io/patterns/data/cars.html

QRS隔离命令和查询
CQRS是命令查询职责隔离(Command Query ResponsibilitySegregation)的简称

它将持久化数据模型和使用数据的模块 分为两部分:命令端和查询端。命令端模块和数据模型实现创建、更新和删除操作(缩写为 CUD,例如:HTTP POST、PUT和DELETE)。查询端模块和数据模型实现查询(例如HTTP GET)。查询端通过订阅命令端发布的事件,使其数据模型与命令端数据模型保持同步。

(Update、Query两个操作分别针对不同的数据库,通过Query查询点数据库来实现查询的聚合操作)

image-20230620095347031

利与弊

C Q R S 既 有 利 也 有 弊 。 好 处 如 下:
• 在徵服务架构中高效地实现查询。
• 高效地实现多种不同的查询类型。
• 在基于事件溯源技术的应用程序中实现查询。 • 更进一步地实现问题隔离。

尽管CQRS 有不少好处,但它也有 一些弊端: 。更加复杂的架构。
• 处理数据复制导致的延迟。 让我们从复杂性增加开始看看这些弊端。

第八章 - 外部API

API 网关模式

请求路由

api组合

协议转化

为每一个客户端提供专有API

在这个例子中,APIGateway 有三个API模块:
• 移动设备API:为FTGO移动客户端实现API。
• 浏览器API:为浏览器中运行的JavaScript 应用程序实现API • 公共A PI :为第三方开发人员实现API 。

image-20230627080319888

实现边缘功能 **

后端前置模式(backend for front)

为每种客户端实现单独的API Gateway

image-20230627080552549

API Gateway模式优缺点

处理局部故障:断路器模式

问题分析

实现

基于图像的API技术(模式驱动的API技术)

能使客户端控制返回数据- — Graph QL

image-20230627081328251

第九章 测试策略 - 微服务测试?

自动化测试通常包括四个阶段(http:/xunitpatterns..com/Four%20 Phase%20Test.html):

  1. 设置环境:将被测系统以及其他相关元素组成的测试环境初始化为所需的状态。例如,
    创建测试中的类,并将其初始化为呈现特定行为所需的状态。
  2. 执行测试:调用被测系统,例如,在被测试的类上调用一个方法。
  3. 验证结果:对调用的返回结果和被测系统的状态进行判断。例如,验证方法的返回值
    和被测试类的新状态与预期一致。
  4. 清理环境:必要时清理测试环境。许多测试省略了这个阶段,但是某些类型的测试,
    比如涉及数据库的测试可能需要在这个阶段将数据库的状态回滚到设置环境阶段前的初始
    状态。

测试策略

测试象限

image-20230627081712656

测试象限(如图9-4所示)按两个维度对测试进行分类:
• 测试是面向业务还是面向技术:使用领域专家的术语来描述面向业务的测试,使用开发人员的术语和实现来描述面向技术的测试。
• 测试的目标是协助开发还是寻找产品 缺陷:开发人员使用协助开发的测试作为日常工 作的一部分。寻找产品缺陷的测试旨在确
面向业务 定需要改进的部分。

测试金字塔

消费者驱动契约测试

消费者 与 提供者:验证服务的客户端是否能与服务端通信

模式:消费者驱动的契约測试 验证服务是否满足它的消费者的期望

模式:消费者契约测试
验证服务的客户端是否可以与服务通信。

流水线

image-20230627081539577

图 9 - 9 中 显 示 的 示 例 部 署 流 水 线 包 含 以 下阶 段 :
• 提交前测试阶段:执行单元测试。这是由开发人员在提交代码更改之前执行的。 • 提交测试阶段:编译服务,执行单元测试,并执行静态代码分析。
• 集成测试阶段:执行集成测试。
• 组件测试阶段:执行服务的组件测试。
• 部署阶段:將服务部署到生产环境中。

编写单元测试

  • 独立型单元测试: 使用针对类的依赖性的模拟对象隔离测试类。(只测试当前class)
  • 协作型单元测试: 测试一个类及其依赖项(测试当前class 及其 依赖)

image-20230701153906275

为实体编写

set() get() 方法测试

为Sage编写

为领域服务编写

为控制器编写

向Controller发送Http请求的测试,测试Controller是否完整接收和响应

为事件和消息处理编写

第十章 微服务中的测试策略(下)

集成测试编写

  • 集成测试必须验证服务是否可以与其客户端和依赖项 进行 通信。 但是,不是测试整 个服务,而是测试实现通信的各个适配器
  • 集成测试位于单元测试之上。它们验证服务 是否可以与其依赖项进行通信,其中包括 基础设施服务 (如数据库)和应用程序服务

OrderService交互图

image-20230627082931881

—策略

1、测试每个服务的适配器,以及适配器的支持类

2、使用契约(消费者-提供者)

image-20230701154614704

—为持久层编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Runwith (SpringRunner.class)
@SpringBootTest (classes = OrderJpaTestConfiguration.class) public class OrderJpaTest {
@Autowired
private OrderRepository orderRepository;
@Autowired
private TransactionTemplate transactiontemplate;
@Test
public void shouldSaveAndLoadOrder() {
Long orderId = transactionTemplate.execute ( (ts) order order
>- {
new Order (CONSUMER_ID, AJANTA_ID, CHICKEN_VINDALOO_LINE_ITEMS); orderRepository. save (order);
return order.getid; );
transactionTemplate.execute((ts) - >
Order order = orderRepository.findById (orderId) .get ();
}
}

—基于RESTful 的请求/响应交互 的集成测试

契约用于验证两端的适配器类,确保API Gateway和or der service之间基 于 R E S T 的 通 信 符 合 契 约。 消 货 者 端 测 试 验 证 o r d e r s e r v i c e P r o x y 是 否 正 确 调用Order service。捉供者端测试验证OrderController 是否正确实现了
RESTAFI 接又

image-20230701155106954

—订阅/发布交互的集成测试

契约用于测试发布/ 订阅交互的双方。提供者端测试验证OrderDomainEvent- Publisher 是否发布确认契约的事件。消費者端测试验证OrderHistory- EventHandlers 是否使用了契约中的示例事件

—针对异步请求/响应式的交互集成契约测试

用于测试异步请 求/ 响应式交互的适配器类的契约。提供 者端测试验证 Ki t c he n- servicecommandHandler 是否处理命令并发出回复。消费者端測试验证Kitchen- s e r v i c e Pr o x y 是否发送符合契约的命令,并处理契约中的示例回复

编写组件测试

到 目 前 为 止, 我 们 已 经 研 究 了 如 何 测 试 单 个 类 和 一 组 相 关 类 。 现 在 想 要 验 证 o r d e r servi ce 是否按预期工作。换句话说,我们希望编写服务的验收测试,將其视为黑盒 并通过调用API 验证服务的行为。

为 服 务 编 写 验 收 测 试 的 更 好 方 法 是 使 用 组 件 测 试

模式:服务组件測试
单独测试服务

—-验收测试

验收测试是针对软件组件的面向业务的测试。它们从组件客户端而不是内部实现的角度 描 述 了所 需 的 外 部 可 见 行 为 。 这 些 测 试 源 自 用 户 故 事 或 用 例 。 例 如 , O r d e r s e r v i c e 的 关 键用例之一是Place order1e。

(编写服务实际对外应用的黑盒测试)

—组件测试

(将服务启动,测试整个服务)

进程 内组件测试

一种选择是编写进程内组件测试。进程内组件测试使用常驻内存的桩和模拟代替其依赖 性来运行服

进程外组件测试

进程外组件 测试使用真实的基础设施服务,例如数据库和消息代理,但是对应用程序服务的任何依赖项 使 用 桩

—编写组件测试

order Service 的组件测试使用Cucumber 测试框架来执行用Gherkin验收测试
DSL编写的测试场景。测试使用Docker 运行Order service 及其基础设施服 务,例如Apache Katka和MysQL

端到端测试

(测试整个应用程序)

组件测试分别测试每个服务,端到端测试会测试整个应用程序。

—设计

正如我已经解释的那样,你应该尽量控制端到端测试的数量。一个好的策略是编写用户 旅程(user journey)测试。用户旅程测试对应于用户使用系统的过程。例如,你可以编写一 个完成所有三项测试的单个测试,而不是单独测试创建订单、修改订单和取消订单。这种方 法可以显著滅少必须编写的测试数量并缩短测试执行时间

总结

  • • 使用契约作为示例消息来驱动服务之间交互的测试。编写测试以验证两个服务的适配器是否符合契约,而不是编写运行两个服务及其传递依赖关系的慢速测试。
  • • 编写组件测试以通过其API验证服务的行为。应该通过单独测试服务来简化和加速组件测试,使用桩来解快其依赖项。
  • 编写用户旅程测试,以最大限度地减少端到端测试的数量,这些测试缓慢、脆弱又耗
    时。用户旅程测试模拟用户在应用程序中的旅程,并验证相对较大的应用程序功能片 段的高级行为。因为测试很少,所以每次测试开销的数量(例如测试设置)被最小化, 这就加快了测试速度。

第十一章 开发面向生产环境的微服务应用

服务质量属性:安全性、可靠配置性、可观测性

开发安全的服务

构如何影响应用程序级别的安全性。 应用程序开发人员主要负责实现安全性的四个不同方面:
,身份验证:验证尝试访问应用程序的应用程序或人员(安全的术语叫主体)的身份。例如, 应用程序通常会验证访问主体的凭据,例如用户的又和密码,或应用程序的API 密钥。

• 访问授权:验证是否允许访问主体对指定数据完成请求的操作。应用程序通常使用基 于角色的安全性和访问控制列表(ACL) 的组合。基于角色的安全性为每个用户分配 一个或多个角色,授子他们调用特定操作的权限。ACL投子用户或角色对特定业务对象或聚合执行操作的权限。
• 审计:跟踪用户在应用中执行的所有操作,以便检测安全问题,帮助客户实现并强制 执行合规性。
• 安全的进程间通 信:理想情况 下,所有进出服务的通信都应该采用传输层安全性 ( TLS) 加密。服务间通信甚至可能需要使用身份验证。

使用 安全框架
正确实现身份验证和访问授权具有挑战性。最好使用经过验证的安全框架。使用哪 个框架取决于你的应用程序的技术栈。流行的框架包括以下几个:
• Spring Security ( https://projects.spring.io/spring-security) :适用于Java 应用程序 的流行框架。它是一个复杂的框架,可以处理身份验证和访问授权。
• Apache Shiro (https://shiro.apache.org):另一个Java 安全框架。
• Passport ( http://www.passportijs.org):在Node.js 应用程序中流行的一个专注于身
份验证的安全框架。

—由 APIGateway处理身份验证

A P I G a t e w a y 调 用 的 服 务 需 要 知 道 发 出 请 求 的 主体 (用 户 的 身 份 ) 。 它 还 必 领 验 证 请 求 是 否已经过通过身份验证。解决方案是让API Gat eway 在每个服务请求中包含 一个令牌。服务 使 用 令 牌 验 证 请 求 , 并 获 取 有 关 主 体 的 信 息 。- A P I G a t e w a y 还 可 以 为 面 向 会 话 的 容 户 端 提 供 相同的令牌,以用作会话令牌。

客户端的事件序列如下:

  1. 客户端发出包含凭据的请求给API Gateway。
  2. APIGateway 对凭据进行身份验证,创建安全令牌,并将其传递给服务。 基于登录的客户端的事件序列如下:
  3. 客户端发出包含凭据的登录请求。
  4. APIGateway 返回安全令牌。
  5. 客户端在调用操作的请求中包含安全令牌。
  6. API Gat eway 验证安全令牌并将其转发给服务。

—处理访问授权

​ 实现访问授权的一个位置是APIGateway。例如 ,它可以将对GET/order s/1orderId) 的访问限制为消货者和客户服务代表。如果不允许用户访问特定路径,则API Gat e wa y 可以 在将请求转发到服务之前拒绝该请求。与身份验证一样,在API Gateway 中集中实现访问授 权可降低安全漏洞的风险。你可以使用安全框架(如SpringSecurity)在API Gateway中实 现访问授权。
​ 在API Gateway中实现访问授权的一个弊端是,它有可能产生API Gateway 与服务之间 的耜合,要求它们以同步的方式进行代码更新。而且,API Gateway 通常只能实现对URL路 径的基于角色的访问。由API Gateway 实现对单个领城对象的访问授权通常是不实际的,因 为这需要详细了解服务的领域逻辑。

另一个实现访问授权的位置是服务。服务可以对URL和服务方法实现基于角色的访问 授权 。 它 还 可 以 实 现 A C L 来 管 理 对 聚 合 的 访 问 。 例 如 , 在 O r d e r s e r v i c e 中 可 以 实 现 基 于角色和基于ACL的授权机制,以控制对order 的访问。FTGO应用程序中的其他服务也 可以实现类似的访问授权逻辑

—使用J WT传递用户身份和角色

image-20230627204438275

图11-4 所示的事件顺序如下:

  1. 容户端发出请求,使用基本身份验证提供它的凭据。
  2. APIGateway向OAuth2.0身份验证服务器发出OAuth2.0密码授子(PasswordGrant)
    请求 (www.oauth.com/oauth2-servers/access-tokens/password-grant/)。
  3. 身份验证服务器验证API 客户端的凭据,并返回访问令牌和刷新令牌。
  4. APIGateway 在其对服务的请求中包含访问令牌。服务验证访问令牌并使用它来授权请求。

image-20230627204052051

事件顺序如下:

  1. 基于登录的客户端将其凭据发送到API Gateway。
  2. APIGateway的LoginHandler 向OAuth 2.0身份验证服务器发出密码授子请求(www.oaut h.com/oaut h2-server s/access-tokens/password-gran t/ )。
  3. 身份验证服务器验证客户端的凭据,并返回访问令牌和刷新令牌。
    4.APIGateway 将访问令牌和刷新令牌返回给容户端,通常是采用cookie的形式。
  4. 客户端在向APIGateway 发出的请求中包含访问令牌和刷新令牌。
  5. APIGateway的session Authentication Interceptor验证访问令牌,并将 其包含在对服务的请求中。

无论你使用哪种方法,三个关键思 想如下:
• APIGateway负责验证客户端的身份。
• APIGateway和服务使用透明令牌(如JWT)来传递有关主体的信息。 • 服务使用令牌获取主体的身份和角色。
现在我们己经了解了如何使服务安全,让我们看看如何使它们可配置。

设计可配置的服务

模式: 外部化配置 在运行时向服务提供配置属性值,例如数据库访问凭据和网络位置

外部化配置机制在运行时向服务实例提供配置属性值。 主要有两种方法:
• 推送模型:部署基础设施通过类似操作系统环境变量或配置文件,将配置属性传递给 服务实例。
• 拉取模型:服务实例从配置服务器读取它所需要的配置属性。

设计可观测性的服务

你可以使用以下模式来设计可观测的服务:
• 健康检查API:公开返回服务运行状况的接又。
• 日志 聚合:记录服务活动并將日志写人集中式日志记录服务器,该服务器提供搜索和告曫。
• 分布式跟踪:为每 一个在服务之间跳转的外部请求分配唯一-1D,并跟踪请求。
• 异常跟踪:向异常跟踪服务报告异常,该异常跟踪服务可以对异常进行重复数据删 除,向开发人员发出警报并跟踪每个异常的解决方案。
• 应用程序指标:服务运维指标,例如计数器和指标,并将它们公开给指标服务器。 • 审核日志记录:记录用户操作。

—模 式: 健 康 检 查 AP I

服务公开健康检查API 接又,例如GBr/health,它返回服务的健康状况

—模式:日志聚合

在 支 持 搜 索 和 告 警 的 集 中 式 数 据 库 中 聚 合 所 有 服 务 的 日 志。

image-20230627083807416

—模式:分布式追踪

为每个外部请求分配一 个唯一的ID, 并在提供可视化和分析的集中式服务器中记录 它 如 何 从 一 个 服 务 流 向 下 一 个 服 务。

显示了分布式追踪术语中称为追踪的内容。追踪表示外部请求,由一个或多个 跨度组成。跨度表示操作,其关键属性是操作的名称、开始时间戳和结束时间。跨度可以有 一个或多个子跨度,表示嵌套操作

  • • ftgo-order-service:应用程序的名称。
  • • 8d8fdc37be104cc6: traceIdo :8d8fdc37be104cc6: spanld。
  • • fa lse:表示此跨度未导出到分布式追踪服务器。

image-20230629215625816

—模式:应用程序指标

服务将指标数据发送给负责聚合、可视化和告警的中央服务器

监控和告警功能是生产环境的关键部分。如图11- 14 所示,监控系统从技术栈的每个部 分收集指标,这些指标提供有关应用程序健康状况的关键信息。指标涵盖的范围从基础设施 相关的指标(如CPU、内存和磁盘利用率)到应用程序级别的指标 ( 如服务请求延迟和执行 的请求数)

—模式:异常追踪

服务把产生的异常报告给中央服务,该服务对异常进行重复數据制除、生成警振并 管理异常的解决方案

—模式:审核日志记录

记录数据库中的用户操作,以帮助容户支持、确保合规性,并检测可疑行为。

有 几 种 不 同 的 方 法 来 实 现 审 计 日 志 记 录:

  • ,将审计日志记录代码添加到业务逻辑中。
  • • 使用面向切面编程。
  • • 使用事件溯源

向 业务逻 辑添加 审计 日志 代码:
第一个也是最直接的选择是在整个服务的业务逻辑中使用审计日志代码。例如,每种服 务方法都可以创建审核日志条日并将其保存在数据库中。这种方法的缺点是它将审计日志代 码和业务逻辑交织在 一起,从而降低了可维护性。另 一个缺点是它可能容易出错,因为它依 赖于开发人员编写审计日志代码。

使用面向切面编程:
第二种选择是使用AOP。你可以使用AOP框架(如Spring AoP)来定义自动拦 截每个服务的方法调用,并持久化审计日志条日。这是一种更可靠的方法,因为它可 以自动记录每个服务方法调用。使用AOP 的主要缺点是只能记录调用的方法名称和它 的参数,因此确定正在执行的业务对象,并生成面向业务的审计日志条目可能具有挑 战性。
使用事件溯源:
第 三个 也 是 最 后 一 个 选 择 是 使 用 事 件 湖 源 来 实 现 你 的 业 务 逻 辑 。 如 第 6 章 所 述 , 事 件 溯 源自动为创建和更新操作提供审计日志。你需要在每个事件中记录用户的身份。但是,使用 事件溯源的 一个限制是它不记录查询。如果你的服务必须为查询创建日志条日,那么你还必 须考虑其他选择。

使用微服务基座开发-服务网格

使用微服务基底
徵服务基底是 一个框架或一组框架,可以处理许多问题,包括:

  • • 外部化配置。
  • • 健康检查。
  • • 应用程序指标。
  • • 服务发现。
  • • 断路器。
  • 分布式追踪。

—模 式: 服务网格

把所有进出服务的网络流量通过一个网络层进行路由,这个网络层负责解决包括断 路器、分布式追踪、服务发现、负载均衡和基于规则的流量路由等具有共性的需求

image-20230629220753755

第十二章 部署微服务

生产环境必须实现四个 关键功能:
• 服务管理接 又:使开发人员能够创建、更新和配置服务。理想情况下,这个接又是 一 个可供命令行和图形部署工具调用的REST API。 。运行时服务管理:确保始终运行着所需数量的服务实例。如果服务实例崩溃或由于某 种原因无法处理请求,则生产环境必须重新启动它。如果运行服务的主机发生崩溃, 则必领在其他主机上重新启动这些服务实例。
• 监控:让开发人员深人了解服务正在做什么,包括日志文件和各种应用指标。如果出现问题,必须提醒开发人员。第11章描述了监控,也称为可观测性。
• 请求路由:将用户的请求路由到服务。

四种主要的部署选项:

  • 。使用编程语言特定的发布包格式部署服务,例如Java JAR或WAR文件。我并不推荐 这种做法,之所以介绍这个选项,是因为这个部署方法有各种显著的缺点,会促使你 思考和选择其他更为合理和现代化的部署技术。
  • • 将服务部署为虚拟机,把服务打包为虚拟机镜像,这个镜像封装了服务的技术栈,这 样可以简化部署。
  • • 将服务部署为容器,这些容器比虚拟机更轻量级。我将展示如何使用流行的Docker 编 排框 架 K u b e r n e t e s 部 署 F T G O 应 用 程 序 的 R e s t a u r a n t s e r v i c e 。
  • • 使用Serverless部署模式部署服务,这比容器更加现代化。我们将研究如何使用AWS L a m b d a (一 种 流 行 的 S e r v e r l e s s 平 台 ) 部 署 R e s t a u r a n t s e r v i c e 。

模式: 编程语言特定的发布包格式
使用特定于编程语言的软件发布包将服务部署到生产环境。

模 式:将服务部署为虛拟机
将作为虛拟机镜像打包的服务部署到生产环境中。每个服务实例都是一个虛拟机。
将服务作为虛拟机的模式具有许多优点:
• 虚拟机镜像封裝了技术栈。
• 隔离的服务实例。
• 使用成熟的云计算基础设施。

模式:将服务部署为容器
特作为容器镜像打包的服务部署到生产环境中。每个服务实例都是一个容器。

Kubernetes

D o c k e r 编 排 框 架 ( 如 K u b e r n e t e s ) 有 三个 主 要 功 能 :

  • • 资源管理:将一组计算机视为由CPU、内存和存储卷构成的资源池,将计算机集群
    视为一台计算机。
  • • 调度:选择要运行容器的机器。默认情况下,调度考志容器的资源需求和每个节点的可用资源。它还可以实现在同一节点上部署具有亲和性(aftini y)的容器,或确保特 定的几个容器分散部署在不同的节点之上(反亲和性,anti-atfinity)。
  • 服务管理:实现命名和版本化服务的概念,这个概念可以直接映射到微服务架 容器 构中的具体服务。编排框架确保妢终运行所需数量的正常实例。它实现请求的
    负载均衡。编排框架也可以执行服务的滚动升级,并允许你回滚到旧版本。

image-20230629221522525

—Kubernetes 的关键概念

正如本节开始提到的,Kubernetes 非常复杂。但是,一旦掌握了一些关键对象的概念, 就可以高效地使用Kubernetes。Kubernetes 定义了许多类型的对象。从开发人员的角度来看, 最重要的对象如下:

  • Pod:Pod 是Kubernetes 的基本部署单元。它由一个或多个共享IP地址和存储卷的容 器组成。服务实例的pod 通常由单个容器组成,例如运行JVM的容器。但在某些情 况下,Pod 包含一个或多个实现支持功能的边车(sidecar )容器。例如,Nginx服务 器可以有一个边车容器,定期执行git pul1以下载最新版本的网站。Pod的生命周 期很短,因为Pod 的容器或它运行的节点可能会崩溃。
  • • Deployment:Pod的声明性规范。**Deployment是一个控制器,可确保始终运行 所需数量的Pod 实例(服务实例)**。它通过滚动升级和回滚来支持版本控制。稍 后在12. 4. 2 节中,你将看到微服务架构中的每个服务都是Kubernetes 的 一个De- ployment.
  • • Service日:向应用程序服务的客户端提供的一个静态/稳定的网络地址。它是基础设施提供的服务发现的一种形式,如第了章所述。每个Service 具有一个卫地址和一个 可解析为该IP 地址的DNS 名称,并路一 个或多个Pod 对TCP 和UDP 流量进行负载 均衡处理。IP 地址和DNS 名称只能在Kubernetes内部访问。稍后,我将介绍如何配 置可从集群外部访问的服务。
  • • ConfigMap:名称与值对的命名集合,用于定义一个或多个应用程序服务的外部化配 置 ( 有 关 外 部 化 配 置 的 概 述 , 请 参 阅 第 1 1 章 )。 P o d 容 器 的 定 义 可 以 引 用 C o n f i g M a p 来定义容器的环境变量。它还可以使用ConfigMap 在容器内创建配置文件。可以使 用Secret 来存储敏感信息(如密码),它也是ConfigMap的一种形式。

—Yaml配置信息

部署Pod:

代码清单12-4 用于f go-restaurant-service的kubernetes部署

代码清单12- 4是定义 R e s t a u r a n t s e r v i c e 部 署 对 象 的 Y A M L 文件 。 此 部 署 指 定 运 行 P o d 的 两 个 副 本 。 P o d 只有一个容器。

image-20230629221857389

部署Service:

S e r v i c e 日 也 是 一 个 K u b e r n e t e s 对 象 , 它 为一 个 或 多 个 P o d 的 客 户 端 提 供 稳 定 的 网 络 访 问 端点。它具有IP地址和解析该IP地址的DNS名称。服务跨Pod 对到该IP地址的流量进行 负 载 均 衡 处 理

image-20230629222159940

—流水线部署

推出新版本的一种更可靠的方法是将部署流程与发布流程分开:

  • • 部署流程:让服务开始在生产环境中运行。
  • • 发布流程:使最終用户可以使用(访问)服务。

我们使用以下步骤将服务部署到生产环境中:

  1. 将新版本部署到生产环境中,而不向其路由任何最终用户请求。

  2. 在生产中进行测试。

  3. 將其发布给少数最终用户。

  4. 逐步将其发布给越来越多的用户,直到它处理所有生产流量为止。

  5. 任何时候出现问题,请恢复旧版本,否则, 一旦你确信新版本正常工作,请删除旧 版 本。

服务网格Istio

Istio 具有丰富的功能,分为四大类:

  • • 流量管理:包括服务发现、负载均衡、路由规则和断路器。
  • • 通信安全:使用传输层安全性(TLS)保护服务间通信。
  • • 遥测(Telemetry):捕获有关网络流量的指标并实施分布式跟踪。
  • • 策略执行:强制实施配额和费率限制。

—架构

图 1 2 - 1 1 显 示 了 I s t i o 的 架构 。 它 由 控 制 平 面 和 数 据 平 面 组 成 。 控 制 平 面 实 现 管 理 功 能 , 包括配置数据平面处理流量路由。数据平面由Envoy 代理组成,每个服务实例 一个。

image-20230629222943434

控制平面的两个主要组成部分是Pilot 和Mixer。

  • Pilot 从底层基础设施中提取有关已部署 服务的信息。例如,当在Kubernetes 上运行时,Pilot 会检素服务和健康Pod。它根据定义的 路由规则配置Envoy 代理以路由流量。
  • Mixer 从Envoy 代理收集遥测信息并执行策略。
  • Istio Envoy代理是Envoy (www.envoyproxyio)的修改版本。它是一种高性能代理,支持 各种协议,包括TCP、HTTP 和HTTPS 等低级协议以及更高级别的协议。它还支持MongoDB、 Redis 和DynamoDB协议。Envoy还支持强大的服务间通信,具有断路器、速率限制和自动重试等功能。它可以通过使用TLS进行Envoy 通信来保证应用程序内的安全通信

—Envoy (sidecar)

模式:边车 在与服务实例一起运行的边车进程或容器中实现服务运行时需要的一些公共功能。

I s t i o 使 用 E n v o y 作 为 边 车 ( s i d e c a r ), 边 车 是 一个 与 服 务 实 例 并 行 运 行 的 进 程 或 容 器,负责实现服务运行时需要的一些公共功能。

在Kubernetes 上运行时,Envoy代理是 服务的Pod 中的一个容器。在没有Pod 概念的其他环境中,Envoy 在与服务相同的容器 中运行。进出服务的所有流量都流经其Envoy 代理,该代理根据控制平面给出的路由规 则来路由流量。例如,通信模式从直接的服务一一服务,变为服务一,Envoy 源- ,Envoy 目标一, 服务。

—使用Istio 部署服务

I s t i o 对Kubernetes服务和Pod 有一些要求:

  • • Kubernetes服务端又必须使用[- 〕的Istio命名约定,其中 protocol 是http、http2、grpc、mongo或redis。如果端又未命名,则Istio 会将端又视为TCP 端又,并且不会应用基于规则的路由。
  • 一 个 P o d 应 该 有 一 个 a p p 标 签 , 例 如 a p p : f t g o - c o n s u me r - s e r v i c e , 用 于 标 识 服务,以支持Istio 分布式跟踪。
  • • 为了同时运行多个版本的服务,Kubernetes部署的名称必领包含版本,例如ftgo- c o n s u m e r - s e r v i c e - v 1 、f t g o - c o n s u m e r - s e r v i c e - v 2 等 。 部 署 的 P o d 应 该 有 一个version 标签,例如version:v1,用来指定版本,以便Istio可以路由到特 定版本。
  • image-20230629223609170

—启动边车代理

  • 第 一种 是 使 用 手动 边 车 注 人 ( m a n u a l s i d e c a r i n j e c t i o n ) 并 运 行 i s t i o c t 1 k u b e - i n j e c t 命 令 : istioctl kube-inject - f ftgo-consumer-service/src/deployment/kubernetes/ftgo-consumer-service.yml | kubecti apply £- -
    此命令读取Kubernetes YAML 文件并输出包含Envoy 代理的已修改配置。然后将修改后 的配置传送到kubect1 applyo

  • 将Envoy边车添加到Pod的第二种方法是使用自动边车注人(automaticsidecar injection)。 启用此功能后,使用kubect1 apply部署服务。Kubernetes自动调用Istio来修改Pod定 义以包含Envoy 代理。

serverless -无服务模式

—开发流程

要部署无服务器组件,需要执行以下步骤:

  1. 选择无服务器平台:选择适合你的需求的无服务器平台,如AWS Lambda、Azure Functions、Google Cloud Functions等。每个平台都有其特定的部署和管理方式。

  2. 准备函数代码:编写你的函数代码,根据平台的要求进行配置和代码编写。函数代码通常是处理事件的主要逻辑。

  3. 确保依赖项:检查函数代码中的依赖项,并确保它们被正确安装。这可能需要将依赖项包含在函数的构建文件或配置文件中。

  4. 打包函数:根据平台的要求,将函数代码打包成一个可执行文件,通常是一个压缩文件(如zip)。

  5. 创建函数:在无服务器平台上创建一个新的函数,指定函数的名称、运行时环境和所需配置。这些平台通常都提供了一个控制台界面或命令行工具来管理函数的创建。

  6. 上传函数代码:将打包好的函数代码上传到无服务器平台。这可以通过控制台界面、命令行工具或API来完成。

  7. 配置触发器:为函数配置触发器,以指定函数应该在哪些事件(如HTTP请求、数据库更改、定时触发器等)发生时被触发。这也可以通过无服务器平台的控制台界面或命令行工具进行配置。

  8. 配置函数参数:根据需要,配置函数的参数和环境变量。这可以包括访问密钥、数据库连接字符串、配置设置等。

  9. 测试函数:在部署之前,确保测试函数的功能和正确性。这可以通过在本地模拟事件或使用平台提供的测试工具来完成。

  10. 部署函数:最后,部署函数到无服务器平台。这将触发平台为函数创建和管理实例,并在有事件触发时运行函数代码。

注意,每个无服务器平台的具体部署步骤可能会略有不同。按照所选平台的文档和指南,详细了解其特定的部署过程和工具。

—架构

该服务的架构如图 12- 14 所示,与传统服务的架构非常相似。 主要区别在于Spri ng MVC 控制器已被AWS Lamb da 请求处理程序类所取代。 其余的业务逻辑没有变化。

该服务由表现层和业务层组成。

  • 表现层包括请求处理程序,请求处理程序由AWS Lambda调用以处理HTTP请求。

  • 业务层包括Restaurantservice、Restaurant JPA实 体 和 R e s t a u r a n t R e p o s i t o r y , 它 封 装 了数 据 库

image-20230629225828794

—部署service 为 lambda

1、开发lambda – handler 表现层

2、打包压缩service为zip

3、编写yaml部署 service 为 lambda

使用AWS 提供的 工具来部署Lambda 两数并配置API Gateway 非常烦琐。幸运的是, 名为Serverles 日的开源项目使部署和管理Lambda 两数变得更加容易。使用Serverless 时, 你需要编写一个简单的serverless. ym1文件,该文件定义了Lambda两数及其RESTfal 端点。然后,Serverless 部署Lambda 两数,并创建和配置将请求路由到它们的API Gateway。 代码清单12-12是将Restaurant service部署为Lambda的serverless.yml的 摘录。

image-20230629230100383

第十三章 微服务架构的重构策略

本章小结
• 在迁移到微服务架构之前,确保你的软件交付问题是由于业务需求超出;单体架构承载 能力而导致的。在架构重构之前,你可以通过改进软件开发过程来加速交付。
• 通过逐步开发一个绞杀者应用程序来迁移到微服务非常重要。绞杀者应用程序是一个 新的应用程序,由围绕现有单体应用构建的微服务组成。你应该尽早并经常证明自己 的价值,以确保业务团队支持迁移工作。
• 將微服务引人架构的一个好方法是将新功能作为服务实现。这样做可以使你使用现代 技术和开发过程快速轻松地开发功能。这是快速展示迁移到微服务价值的好方法。
• 打破单体结构的一种方法是将表现层与后端隔离,这会产生两个较小的单体结构。虽 然这不是一个巨大的改进,但它确实意味着你可以独立部署每个单体。例如,这允许 用户界面团以更轻松地在用户界面设计上进行迭代,而不会彩响后端。
• 打破单体的主要方法是逐步将功能从单体转移到服务中。重点是提取提供最大利益的 服务。例如,如果提取实现正在积极开发功能的服务,你将加快开发速度。
• 新开发的服务几乎总是必须与单体交互。服务通常需要访问单体的数据并调用其功 能。单体有时需要访问服务的数据并调用其功能。要实现此协作,需要开发集成胶 水 ,其中包含单体的人站和出站适配器。
• 为了防止单体的领域模型污染服务的领域模型,集成胶水应该使用反腐层,这是一个 在领域模型之间进行转换的软件层。
• 最小化对提取服务的单体结构的影响的一种方法是将移动到服务的数据复制回单体的 数据库。由于单体的数据库模式保持不变,因此无须对单体代码库进行潜在的大范围 修改。
• 开发服务通常需要你实现涉及单体的Saga。但实现可补偿性事务可能具有挑战性, 需要对单体进行大范围的修改。因此,有时你需要仔细设计服务的提取顺序,以避免 在单体中实现可补偿事务。
• 在重构为微服务架构时,你需要同时支持单体应用的现有安全机制,该机制通常基于 内存的会话,以及服务使用的基于令牌的安全机制。幸运的是, 一个简单的解决方案 是修改单体的登录处理程序以生成包含安全令牌的cookie,然后由API Gateway转发 给服务。

其他

领域驱动设计

介绍来源

领域驱动设计(Domain Driven Design,DDD)是一种软件设计方法论,它提倡将业务领域作为一个核心的概念,并将其理解为一个独特的语言和模型。DDD 建议采用一种聚焦于业务领域的方式来解决软件设计问题,通过对问题域进行深入分析和推理,摸清业务需求的脉络,推导领域模型,打造高度内聚和低耦合的领域模型,提高系统表达能力,使得代码和业务更加贴合。DDD 认为项目失败的原因在于没有正确理解和解决业务领域的问题,因此,领域驱动设计的目的是为了帮助开发者和领域专家协同工作来实现更好的软件设计和代码的实现。

书籍梗概

领域驱动设计(Domain Driven Design,DDD)是由 Eric Evans 在他的著作 “Domain-Driven Design: Tackling Complexity in the Heart of Software” (中文译作《领域驱动设计-软件核心复杂性应对之道》)中提出的一种软件设计方法论。该书首次出版于 2003 年,成为近年来领域驱动设计领域的标志性著作,受到了广泛的关注和认可。自此之后,领域驱动设计已经逐渐成为了设计复杂软件系统的主流方法。此外,该方法还得到了 Martin Fowler、Robert C.Martin 等知名软件工程师的支持和推荐。

《Domain-Driven Design: Tackling Complexity in the Heart of Software》是一本著名的领域驱动设计方法论书籍,共分为三个部分和十八个章节:

第一部分:领域驱动设计的基础

1.领域驱动设计

本章介绍了领域驱动设计的基本原则和核心概念。其中,领域是关于一个问题领域的知识,是进行领域驱动设计的核心,而领域模型是解决业务问题的关键步骤。

2.开发团队和领域专家

本章重点讲述了开发团队和领域专家之间的合作。如果使用领域驱动设计,必须充分利用领域专家,他们具有丰富的业务知识和经验。领域专家和开发团队之间的合作有利于建立共同的领域模型,使开发人员能够更好地理解领域。

3.过程和交互

本章介绍了一些领域驱动设计的基本模式和概念。其中,建模会议和上下文映射是领域模型建立过程中不可或缺的一部分。开发人员需要对领域内的各种过程和交互进行建模,包括时间序列模型、状态模型和活动模型等。

第二部分:战术设计

第二部分介绍了领域驱动设计的战术设计部分,包括应用服务、实体、值对象、领域事件和聚合等概念。

4.对象的作用

本章介绍了几个重要的概念,如实体、值对象和服务,并讨论了它们在领域驱动设计中的作用。一个领域模型通常由许多对象组成,这些对象需要根据其作用进行不同的设计。

5.领域模型和实现策略

本章介绍了如何在领域模型和实现策略之间建立联系。领域模型由业务需求驱动,因此在设计模型时需要注意问题域中的业务需求。实现策略与技术平台、团队规范和其他因素紧密相关,因此也需要与领域模型合理搭配。

6.聚合

本章重点讲述了聚合在领域驱动设计中的应用。聚合是一组相关联的对象,它们作为单个单元进行管理,聚合内部的对象是紧密耦合的,除了聚合外的对象不应该直接访问。

7.领域服务

本章介绍了领域服务的概念,以及在领域驱动设计中如何使用和实现领域服务。领域服务是独立的,它们提供了在领域模型中无法表示的操作,或者该操作太复杂以致它不应简单地被分配给单个实体或值对象。

8.实现聚合

本章讨论了实现聚合时需要注意的问题,包括聚合根和聚合内部的对象如何协同工作、聚合的生命周期以及如何实现聚合之间的关联等。

9.领域事件

本章介绍了领域事件和事件驱动架构的基本原理。领域事件是信息的快照,描述了一个领域中的某些操作或状态的变化。

第三部分:战略设计

第三部分是领域驱动设计的战略设计部分,它对领域层次的构成进行了分析和说明。

10.基础知识

本章介绍了领域驱动设计的基础知识,包括上下文(Context)和子域(Subdomain)的概念。一个上下文代表了某个特定领域,而子域是领域的一个子集。

11.上下文映射

本章讨论了上下文映射,它描述了不同上下文之间的关系。在领域驱动设计中,多个上下文之间的交互是非常普遍的,因此需要一种合适的机制来处理它们之间的耦合问题。

12.大型结构和团队分解

本章讨论了如何应对大型系统中领域的复杂性。如何分解系统是一个很重要的问题。领域驱动设计重视团队的通力合作,并努力使团队成员之间的通信最大化。

13.战略设计的过程

本章介绍了领域驱动设计的战略设计过程,这是一系列基于战略设计的步骤。该过程分为分析、策略定义、方案图以及实现过程。

14.环境和经济因素

本章讨论了环境和经济条件的影响。在领域驱动设计中,考虑到环境和经济因素因素可能会对软件设计产生影响,开发团队在工作时应该充分考虑它们。

15.细粒度的设计

本章介绍了细粒度化的设计方法,它将基本的领域驱动设计概念应用到具体的编程细节当中。编写实现细节时,开发团队应该按照领域模型和战术模式进行编写。

16.集成

本章讨论了集成问题,包括如何在不同的子域之间实现集成,并介绍了一些通用的集成模式。

17.应用架构

本章讨论了如何应用架构考虑领域驱动设计的原则。在应用架构设计上,领域驱动设计可以提供一些关键的应用程序架构指导。

18.实施DDD

最后一章提供了一些有关领域驱动设计实施的建议。要成功实施DDD,需要对团队和组织进行合适的培训和教育,同时需要不断优化软件开发过程和管理结构。


微服务架构设计模式
http://example.com/2023/06/01/模式-思想/微服务架构设计模式/
作者
where
发布于
2023年6月1日
许可协议