微服务设计模式:基本架构和设计指南

微服务设计模式:基本架构和设计指南

了解微服务软件架构的设计模式,以克服诸如松耦合服务、定义数据库等挑战。

微服务架构已成为现代应用开发的事实上的选择。尽管它解决了许多问题,但它并不是万能的。与所有软件一样,它有自己独特的一组挑战需要解决。这就需要学习微服务中的常见设计模式,并通过可重用的解决方案来解决这些挑战。
在深入探讨设计模式之前,了解微服务架构所建立的核心原则非常重要:
微服务设计模式:基本架构和设计指南_第1张图片
图1*: 微服务架构核心实践*
应用这些原则通常会带来一些挑战和问题。在本文中,我们将分解关键的微服务模式、它们解决的常见问题以及它们提供的解决方案。
本指南可以作为构建微服务架构时的重要参考,帮助您有效地设计系统。让我们开始吧。

分解设计模式

模式 #1: 按业务能力分解

问题

微服务的核心是使服务松散耦合,从而应用单一职责原则。然而,将一个应用程序分解成更小的部分必须是逻辑性的。我们如何将一个应用程序分解成小服务?

解决方案

一种策略是按业务能力分解。业务能力是企业为创造价值而进行的一项活动。给定业务的能力集取决于业务类型。例如,保险公司的能力通常包括销售、营销、承保、理赔处理、账单、合规等。每个业务能力可以被视为一个服务——只是它是面向业务的而不是技术性的。

模式 #2: 按子域分解

问题

按业务能力分解应用程序可能是一个好的开始,但你会遇到所谓的"上帝类",这些类在多个服务之间是通用的,难以分解。例如,Order类将在订单管理、订单接收、订单交付等多个服务中使用。我们如何分解它们?

解决方案

对于"上帝类"的问题,领域驱动设计(DDD)可以提供帮助。DDD使用子域和有界上下文概念来解决这个问题。DDD将为企业创建的整个领域模型分解为子域。每个子域都有一个模型,该模型的范围称为有界上下文。每个微服务将围绕有界上下文进行开发。
注意: 识别子域并非易事,需要对业务有深入了解。像业务能力一样,通过分析业务及其组织结构并识别不同领域来确定子域。

模式 #3: 绞杀者模式

问题

到目前为止,我们讨论的设计模式都是针对新建项目(greenfield)进行分解,但我们大多数工作是针对现有的大型单体应用程序(brownfield)。将上述所有设计模式应用于这些大型单体应用程序将非常困难——在使用过程中将它们拆分成更小的部分是一项艰巨的任务。

解决方案

绞杀者模式可以提供帮助。绞杀者模式基于一种类似葡萄藤缠绕树木并逐渐“扼杀”它们的比喻。这种解决方案非常适用于Web应用程序,其中调用会往返,对于每个URI调用,可以将一个服务拆分成不同领域并作为独立服务托管。这个想法是一次处理一个领域。这会在相同URI空间中创建两个独立的应用程序并存。最终,新重构的应用程序“扼杀”或替换原始应用程序,直到最终可以关闭单体应用程序。

集成模式

模式 #4: API网关模式

问题

当一个应用程序被拆分成更小的微服务时,有几个问题需要解决:

  1. 如何调用多个微服务而无需了解生产者的信息。
  2. 在不同渠道(如桌面、移动设备和平板电脑)上,应用程序需要响应相同后端服务但可能需要不同的数据,因为UI可能不同。
  3. 不同消费者可能需要从可重用微服务中获得不同格式的响应。谁来进行数据转换或字段操作?
  4. 如何处理不同类型的协议——其中一些可能不被生产者微服务支持。
解决方案

API网关有助于解决许多由于实施微服务而产生的问题,不仅限于上述问题:
5. API网关是任何微服务调用的唯一入口点。
6. 它可以作为代理服务,将请求路由到相关微服务,抽象出生产者细节。
7. 它可以将请求分发到多个服务并聚合结果后发送回消费者。
8. 通用API无法满足所有消费者需求;此解决方案可以为每种特定类型客户端创建细粒度API。
9. 它还可以将协议请求(例如AMQP)转换为另一个协议(例如HTTP),反之亦然,以便生产者和消费者能够处理。
10. 它还可以卸载微服务的身份验证/授权责任。

模式 #5: 聚合器模式

问题

如上所述,在API网关模式下,我们面临聚合数据的问题。当将业务功能拆分成几个较小逻辑代码段时,有必要考虑如何协作每个服务返回的数据。这一责任不能留给消费者,因为这样可能需要了解生产者应用程序的内部实现。

解决方案

聚合器模式有助于解决这个问题。它讨论了如何从不同服务聚合数据,然后发送最终响应给消费者。这可以通过两种方式完成:
11. 组合微服务 将调用所有必需的微服务,整合数据并在发送回之前转换数据。
12. API网关 也可以将请求划分给多个微服务,并在发送给消费者之前聚合数据。
建议如果要应用任何业务逻辑,则选择组合微服务。否则,API网关是公认的解决方案。

模式 #6: 客户端UI组合

问题

当通过分解业务能力/子域开发服务时,负责用户体验的服务必须从多个微服务中提取数据。在单体架构中,通常只有一个从UI到后端服务的调用来检索所有数据并刷新/提交UI页面。然而,现在情况不再如此,因此我们需要了解如何做到这一点。

解决方案

对于微服务,UI必须设计成具有多个部分/区域的骨架,每个部分将调用单个后端微服务以提取数据。这被称为针对服务的UI组件组合。像AngularJS和ReactJS这样的框架可以轻松实现这一点。这些屏幕被称为单页应用程序(SPA)。这使得应用程序能够刷新屏幕的特定区域而不是整个页面。

数据库模式

模式 #7: 每个服务一个数据库

问题

团队经常面临如何为微服务定义其数据库架构的挑战。以下是必须解决的问题:

  1. 服务必须松散耦合。它们可以独立开发、部署和扩展。
  2. 业务事务可能会强制执行跨多个服务的约束。
  3. 一些业务事务需要查询由多个服务拥有的数据。
  4. 有时必须复制和分片数据库以进行扩展。
  5. 不同服务有不同的数据存储需求。
#### **解决方案**

要解决上述问题,必须设计每个微服务一个数据库。它必须是该服务专用的,并且只能通过该微服务API访问。不能直接由其他服务访问。
例如,对于关系型数据库,我们可以使用每个服务私有表、每个服务一个模式或每个服务一个数据库服务器。每个微服务应该有一个单独的数据库ID,以便可以提供单独的访问权限,设置屏障并防止它使用其他服务的表。
![Database per service microservice architectural diagram](https://img-blog.csdnimg.cn/img_convert/2b6146cd088503adcc59d90d615e4bdd.png)
***图2****: 每个服务一个数据库架构*

### 模式 #8: 每个服务共享数据库

#### **问题**

我们已经讨论了每个服务一个数据库是理想的微服务,但这仅在应用程序是新建项目且采用DDD时才可能。如果应用程序是单体应用并试图拆分成微服务,非规范化并不容易。那么在这种情况下最合适的架构是什么?

#### **解决方案**

每个服务共享数据库不是理想的,但这是上述场景中的可行解决方案。大多数人认为这是微服务的反模式,但对于现有项目,这是将应用程序拆分成更小逻辑部分的良好起点。
在这种模式中,一个数据库可以与多个微服务对齐,但必须限制在2-3个,否则扩展、自主性和独立性将难以执行:
![Shared database microservices architectural diagram](https://img-blog.csdnimg.cn/img_convert/ad80d4c61a436ab086673725d53c2dd7.png)
***图3****: 共享数据库架构*

### 模式 #9: 命令查询责任分离(CQRS)

#### **问题**

一旦我们实施了每个服务一个数据库,就需要查询,这需要从多个服务联合数据——这是不可能的。那么,我们如何在微服务架构中实现查询?

#### **解决方案**

CQRS建议将应用程序拆分成两个部分——命令端和查询端。命令端处理`Create`、`Update`和`Delete`请求。查询端通过使用物化视图处理查询组件。通常与事件溯源设计模式一起使用,以创建任何数据更改的事件。因此,通过订阅事件流来保持物化视图最新。

### 模式 #10: Saga模式

#### **问题**

当每个服务都有自己的数据库且业务事务跨越多个服务时,我们如何确保跨服务的数据一致性?例如,对于电子商务应用程序,其中客户有信用额度,应用程序必须确保新订单不会超出客户的信用额度。由于订单和客户在不同的数据库中,应用程序不能简单地使用本地ACID事务。

#### **解决方案**

Saga代表由多个子请求组成的高级业务流程,每个子请求在单一服务内更新数据。每个请求都有一个补偿请求,在请求失败时执行。它可以通过两种方式实现:
  1. 编排——当没有中央协调时,每个服务生成并监听另一个服务的事件,并决定是否采取行动。
  2. 仲裁——仲裁者(对象)负责Saga的决策和业务逻辑排序。
## 可观测性模式

接下来,让我们深入探讨微服务可观测性模式。下面是一个用于参考所有可观测性主题的示例微服务架构图。
![Microservices design patterns observability architecture diagram](https://img-blog.csdnimg.cn/img_convert/bda0525cf897673f29bdfa1fc13b527c.png)
***图4****: 微服务可观测性* *图(来源:* [*CNCF*](https://www.cncf.io/blog/2023/11/02/opentelemetry-in-decoupled-event-driven-architectures-solving-for-the-black-box-when-your-consuming-applications-are-constantly-changing/)*)*

### 模式 #11: 日志聚合

#### **问题**

考虑一个应用程序由运行在多台机器上的多个服务实例组成。请求通常跨越多个服务实例。每个服务实例生成标准格式的日志文件。我们如何通过日志了解特定请求的应用程序行为?

#### **解决方案**

我们需要一个集中式日志记录服务,将每个服务实例的日志聚合起来。用户可以搜索和分析日志。他们可以配置在日志中出现某些消息时触发的警报。例如,PCF具有Loggeregator,它收集PCF平台(路由器、控制器、diego等)以及应用程序中的每个组件的日志。AWS Cloud Watch也有类似功能。

### 模式 #12: 性能指标

#### **问题**

当由于微服务架构增加了多个服务组合时,监控事务变得至关重要,以便在出现问题时发送警报。那么我们应如何收集指标以监控应用程序性能?

#### **解决方案**

需要一个指标服务来收集有关各项操作的统计信息。它应该聚合应用程序服务的指标,提供报告和警报。有两种聚合指标模型:
  • 推送——该服务将指标推送到指标服务,例如New Relic、AppDynamics等。

  • 拉取——指标服务器从该服务拉取指标,例如Prometheus。

    模式 #13: 分布式追踪

    问题

    在微服务架构中,请求通常跨越多个服务。每个服务通过执行一个或多个操作来处理请求。那么,我们如何端到端地追踪一个请求以排除故障?

    解决方案

    我们需要一个服务来:

    • 为每个外部请求分配一个唯一的外部请求ID。

    • 将外部请求ID传递给所有服务。

    • 在所有日志消息中包含外部请求ID。

    • 记录关于请求和操作的信息(例如,开始时间、结束时间等),并将其存储在集中式服务中。
      Spring Cloud Sleuth与Zipkin服务器是一个常见的实现示例。

      模式 #14: 健康检查

      问题

      当实施微服务架构时,可能会出现服务已启动但无法处理事务的情况。在这种情况下,如何确保请求不会发送到这些失败的实例?我们可以通过负载均衡模式来解决这个问题。

      解决方案

      每个服务需要有一个端点用于检查应用程序的健康状况,例如/health。这个API应该检查主机状态、与其他服务/基础设施的连接以及任何特定逻辑。
      Spring Boot Actuator实现了/health端点,并且可以自定义实现。

      横切关注点模式

      模式 #15: 外部化配置

      问题

      一个服务通常会调用其他服务和数据库。对于每个环境(如开发、QA、UAT或生产),端点URL或其他配置属性可能不同。任何这些属性的更改可能需要重新构建和重新部署服务。我们如何避免代码修改以进行配置更改?

      解决方案

      外部化配置,包括端点URL和凭证,将解决这个问题。应用程序应该在启动时或动态加载它们。
      Spring Cloud配置服务器提供了将属性外部化到GitHub并作为环境属性加载的选项。这些可以在应用程序启动时访问,也可以在不重启服务器的情况下刷新。

      模式 #16: 服务发现

      问题

      当微服务出现时,我们需要解决几个关于调用服务的问题:

    1. 使用容器技术,IP地址动态分配给服务实例。每次地址变化时,消费者服务可能会中断并需要手动更改。
    2. 每个服务URL必须被消费者记住,并且变得紧密耦合。
      那么消费者或路由器如何知道所有可用的服务实例及其位置?
    解决方案

    需要创建一个服务注册表,它将记录每个生产者服务的元数据。服务实例在启动时应注册到注册表,并在关闭时取消注册。因此,消费者或路由器应查询注册表以查找服务的位置。
    注册表还需要对生产者服务进行健康检查,以确保只有工作正常的服务实例可供使用。服务发现有两种类型:客户端发现服务器端发现。客户端发现的一个例子是Netflix Eureka,服务器端发现的一个例子是AWS ALB。

    模式 #17: 断路器

    问题

    一个服务通常会调用其他服务以检索数据,而下游服务可能会出现故障。这有两个问题:首先,请求将继续发送到故障的服务,耗尽网络资源并降低性能。其次,用户体验将变得不佳且不可预测。我们如何避免级联服务故障并优雅地处理故障?

    解决方案

    消费者应通过代理调用远程服务,该代理行为类似于电路断路器。当连续故障次数超过阈值时,断路器会跳闸,并且在超时时间段内,所有尝试调用远程服务的请求将立即失败。在超时结束后,断路器允许有限数量的测试请求通过。如果这些请求成功,断路器恢复正常操作。否则,如果出现故障,则超时时间段重新开始。
    Netflix Hystrix是断路器模式的良好实现。它还帮助定义回退机制,当断路器跳闸时可以使用。这提供了更好的用户体验。

    模式 #18: 蓝绿部署

    问题

    使用微服务架构,一个应用程序可以有许多微服务。如果我们停止所有服务然后部署增强版本,停机时间可能很长且影响业务。此外,任何回滚都将是一场噩梦。那么我们如何避免或减少部署期间的停机时间?

    解决方案

    蓝绿部署策略可以实现减少或消除停机时间。它通过运行两个相同的生产环境——蓝色和绿色——来实现这一点。假设绿色是现有的实时实例,而蓝色是应用程序的新版本。在任何时候,只有一个环境是实时的,并处理所有生产流量。所有云平台都提供了实现蓝绿部署的选项。

    结论

    还有其他几个关键微服务架构模式,如sidecar模式、链式微服务、分支微服务、事件溯源设计模式、大使模式等等。随着微服务的发展,这个列表还在不断增长,因此我们很想听听您在评论中使用了哪些微服务模式!希望这些内容对您有所帮助!如果有任何问题或需要进一步的解释,请随时告诉我。

你可能感兴趣的:(架构修炼之路,架构,微服务,设计模式)