[译]微服务的设计模式

75 阅读15分钟

微服务设计模式:

聚合器

API网关

连锁或责任链

异步消息传递

数据库或共享数据

事件的采购

分支

查询责任隔离器

断路器

分解

微服务架构已成为现代应用程序开发的实际选择。虽然它解决了某些问题,但它不是万能的。它有几个缺点,在使用这种架构时,有很多问题必须解决。这导致需要学习这些问题的共同模式,并使用可重用的解决方案来解决它们。因此,需要讨论微服务的设计模式。在我们深入研究设计模式之前,我们需要了解微服务架构构建了哪些原则:

  1. 可扩展性
  2. 可用性
  3. 弹性
  4. 独立、自主
  5. 分散治理
  6. 故障隔离
  7. 自动配置
  8. 通过DevOps持续交付

应用所有这些原则会带来一些挑战和问题。让我们讨论一下这些问题及其解决方案。

1. 分解模式

a. 按业务能力分解

问题

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

解决方案

一种策略是因业务能力而分解。业务能力是企业为创造价值而做的事情。给定业务的一组功能取决于业务类型。例如,保险公司的能力通常包括销售、营销、承销、索赔处理、计费、合规等。除了面向业务而不是技术之外,每种业务能力都可以被视为一种服务。

b. 按子域分解

问题

使用业务功能分解应用程序可能是一个良好的开端,但您会遇到所谓的“上帝类”,这不容易分解。这些类将在多个服务中很常见。例如,订单类将用于订单管理、订单处理、订单交付等。我们如何分解它们?

解决方案

对于“上帝类”问题,DDD(域驱动设计)前来救援。它使用子域和边界上下文概念来解决这个问题。DDD将为企业创建的整个域模型分解为子域。每个子域将有一个模型,该模型的范围将称为边界上下文。每个微服务都将围绕边界上下文开发。

注意:识别子域不是一件容易的事情。这需要了解业务。与业务能力一样,子域是通过分析业务及其组织结构以及确定不同专业知识领域来确定的。

c. 扼杀者模式

问题

到目前为止,我们谈论的设计模式是分解未开发应用,但我们所做的80%的工作是已开发的应用,这些应用是大型的整体应用。将上述所有设计模式应用于它们将很困难,因为在实际使用的同时将它们分解成更小的部分是一项艰巨的任务。

解决方案

扼杀者模式来救援。扼杀者图案基于对藤蔓的类比,藤蔓勒死包裹着的一棵树。该解决方案适用于Web应用程序,其中调用来回进行,对于每个URI调用,服务可以分解为不同的域并作为单独的服务托管。这个想法是一次做一个域。这创建了两个独立的应用程序,并排在同一URI空间中。最终,新重构的应用程序“扼杀”或替换原始应用程序,直到您最终可以关闭整体应用程序。

2. 集成模式

a. API网关模式

问题

当应用程序分解为较小的微服务时,需要解决一些问题:

  1. 如何调用多个抽象生产者信息的微服务。
  2. 在不同的渠道(如桌面、移动和平板电脑)上,应用程序需要不同的数据来响应相同的后端服务,因为用户界面可能不同。
  3. 不同的消费者可能需要来自可重用微服务的不同格式的响应。谁来进行数据转换或现场操作?
  4. 如何处理不同类型的协议,其中一些协议可能不受生产者微服务的支持。

解决方案

API网关有助于解决微服务实现引起的许多问题,而不限于上述问题。

  1. API网关是任何微服务调用的单点入口。
  2. 它可以作为代理服务将请求路由到相关的微服务,抽象生产者的详细信息。
  3. 它可以将请求分散到多个服务,并聚合结果发送回使用者。
  4. 一刀切的API无法解决消费者的所有需求;该解决方案可以为每种特定类型的客户端创建一个细粒度API。
  5. 它还可以转换协议请求(例如AMQP)到另一个协议(例如HTTP),反之亦然,以便生产者和消费者可以处理它。
  6. 它还可以卸载微服务的身份验证/授权责任。

b. 聚合器模式

问题

我们已经讨论了解决API网关模式中的聚合数据问题。然而,我们将在这里全面讨论它。当将业务功能分解为几个较小的逻辑代码时,有必要考虑如何协作每个服务返回的数据。这一责任不能留给消费者,因为那时它可能需要了解生产者应用程序的内部实现。

解决方案

聚合器模式有助于解决这个问题。它讨论了我们如何汇总来自不同服务的数据,然后向消费者发送最终回复。这可以通过两种方式完成:

1。复合微服务将调用所有所需的微服务,整合数据,并在发送回之前转换数据。

2。API网关还可以将请求分发到多个微服务,并在发送给消费者之前汇总数据。

如果要应用任何业务逻辑,建议选择复合微服务。否则,API网关是既定的解决方案。

c. 客户端UI组成模式

问题

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

解决方案

使用微服务,用户界面必须设计为具有屏幕/页面多个部分/区域的骨架。每个部分将调用单个后端微服务来提取数据。这被称为编写特定于服务的UI组件。AngularJS和ReactJS等框架有助于轻松做到这一点。这些屏幕被称为单页应用程序(SPA)。这使应用程序能够刷新屏幕的特定区域,而不是整个页面。

3. 数据库模式

a. 服务/数据库

问题

如何为微服务定义数据库架构存在的问题。以下是需要解决的问题:

1。服务必须松散耦合。它们可以独立开发、部署和扩展。

2。业务交易可能会强制执行跨越多个服务的不变量。

3。一些业务交易需要查询由多个服务拥有的数据。

4。数据库有时必须复制和分片才能扩展。

5。不同的服务有不同的数据存储要求。

解决方案

为了解决上述问题,每个微服务必须设计一个数据库;它必须仅对该服务具有私有性。它只能由微服务API访问。其他服务无法直接访问它。例如,对于关系数据库,我们可以使用private-tables-per-service、schema-per-service或database-server-per-service。每个微服务都应该有一个单独的数据库ID,以便可以给予单独的访问权限,以设置障碍并防止它使用其他服务表。

b. 每项服务的共享数据库

问题

我们已经讨论过,每个服务一个数据库是微服务的理想选择,但当应用程序是新开发并要与DDD一起开发时,这是可能的。但是,如果应用程序是一个庞然大物,并试图进入微服务,那么去规范化就没那么容易了。在这种情况下,合适的架构是什么?

解决方案

每项服务的共享数据库并不理想,但这是上述场景的工作解决方案。大多数人认为这是微服务的反模式,但对于已开发的应用程序来说,这是将应用程序分解成更小逻辑部分的良好开端。这不应该适用于新开发的申请。在这种模式下,一个数据库可以与多个微服务保持一致,但必须限制在2-3个最大值,否则扩展、自主性和独立性将难以执行。

c. 命令查询责任隔离(CQRS)

问题

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

解决方案

CQRS建议将应用程序分为两部分——命令端和查询端。命令端处理创建、更新和删除请求。查询端使用实例化视图处理查询部分。事件源模式通常与它一起用于为任何数据更改创建事件。通过订阅事件流,物化视图会不断更新。

d. Saga模式

问题

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

解决方案

Saga代表一个高级业务流程,由多个子请求组成,每个子请求都在单个服务中更新数据。每个请求都有一个补偿请求,该请求在请求失败时执行。它可以通过两种方式实现:

  1. 编舞—当没有中央协调时,每个服务都会制作和收听其他服务的活动,并决定是否应该采取行动。

image.png

  1. 编排—编排员(对象)负责事件的决策和业务逻辑排序。

image.png

4. 可观察性模式

a. 日志聚合

问题

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

解决方案

我们需要一个集中的日志服务,该服务汇总每个服务实例的日志。用户可以搜索和分析日志。他们可以配置当某些消息出现在日志中时触发的警报。例如,PCF确实有Loggeregator,它从PCF平台的每个组件(路由器、控制器、迭戈等)以及应用程序中收集日志。AWS Cloud Watch也这样做。

b. 绩效指标

问题

当服务组合由于微服务体系结构而增加时,密切关注事务,以便在问题发生时监视模式并发出警报就变得至关重要。我们应该如何收集指标来监控应用程序的性能?

解决方案

需要指标服务来收集有关单个操作的统计数据。它应该汇总应用程序服务的指标,提供报告和警报。汇总指标有两种模型:

  • 推送—该服务将指标推送到指标服务,例如NewRelic,AppDynamics
  • 拉取—指标服务从服务中提取指标,例如普罗米修斯

c. 分布式跟踪

问题

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

解决方案

我们需要一项服务

  • 为每个外部请求分配一个唯一的外部请求ID。
  • 将外部请求ID传递给所有服务。
  • 在所有日志消息中包含外部请求ID。
  • 记录有关在集中服务中处理外部请求时执行的请求和操作的信息(例如开始时间、结束时间)。

Spring Cloud Slueth和Zipkin服务器是一个常见的实现。

d. 健康检查

问题

当微服务架构实现时,服务可能会出现,但无法处理事务。在这种情况下,您如何确保请求不会进入那些失败的实例?具有负载平衡模式实现。

解决方案

每个服务都需要有一个端点,可用于检查应用程序的运行状况,例如/health。此API应检查主机的状态、与其他服务/基础设施的连接以及任何特定的逻辑。

Spring Boot Actuator确实实现了/健康端点,并且可以自定义实现。

5. 交叉关注模式

a. 外部配置

问题

服务通常也调用其他服务和数据库。对于dev、QA、UAT、prod、端点URL或某些配置属性等每个环境可能有所不同。更改任何这些属性可能需要重建和重新部署服务。我们如何避免更改配置的代码修改?

解决方案

将所有配置外部化,包括端点URL和凭据。应用程序应该在启动时或运行中加载它们。

Spring Cloud配置服务器提供了将属性外部化到GitHub并将其作为环境属性加载的选项。这些可以在启动时由应用程序访问,也可以在不重新启动服务器的情况下刷新。

b. 服务发现模式

问题

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

  1. 使用容器技术,IP地址被动态分配给服务实例。每次地址更改时,消费者服务都会中断,需要手动更改。
  2. 每个服务URL都必须被消费者记住,并紧密耦合。

那么,消费者或路由器如何知道所有可用的服务实例和位置?

解决方案

需要创建一个服务注册表,以保留每个生产者服务的元数据。服务实例应在启动时注册到注册表,并在关闭时取消注册。消费者或路由器应该查询注册表并了解服务的位置。注册处还需要对生产者服务进行健康检查,以确保只有服务的工作实例才能通过它使用。服务发现有两种类型:客户端和服务器端。客户端发现的一个例子是Netflix Eureka,服务器端发现的一个例子是AWS ALB。

image.png

c. 熔断器模式

问题

服务通常调用其他服务来检索数据,下游服务可能会停机。这有两个问题:首先,请求将继续停机服务,耗尽网络资源并降低性能。其次,用户体验将是糟糕和不可预测的。我们如何避免级联服务故障并优雅地处理故障?

解决方案

消费者应通过与熔断器类似行为的代理调用远程服务。当连续故障数量超过阈值时,熔断器会触发,在超时期间,所有调用远程服务的尝试都将立即失败。超时过期后,熔断器允许通过数量有限的测试请求。如果这些请求成功,熔断器将恢复正常运行。否则,如果出现故障,超时期将再次开始。

Netflix Hystrix是熔断器模式的良好实现。它还可以帮助您定义一个后备机制,该机制可以在断路器跳闸时使用。这提供了更好的用户体验。

image.png

d. 蓝绿部署模式

问题

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

解决方案

可以实施蓝绿色部署策略来减少或消除停机时间。它通过运行两个相同的生产环境来实现这一点,蓝色和绿色。让我们假设Green是现有的实时实例,Blue是应用程序的新版本。在任何时候,只有一个环境是实时的,现场环境为所有生产流量服务。所有云平台都提供了实施蓝绿色部署的选项。有关此主题的更多详细信息,请查看本文

image.png