微服务架构十大设计模式详解
微服务设计模式:
- 分解
- 接口网关
- 聚合
- 链式或责任链
- 异步消息传递
- 数据库或共享数据
- 事件溯源
- 分支
- 命令查询责任隔离器
- 断路器
微服务架构已成为现代应用程序开发的实际选择。虽然它解决了我们开发中不少问题,但它并不是完美无瑕,也是有不少缺点。在使用此架构时,必须解决注意采用不同手段来解决好这些问题。这就需要学习这些问题中的常见模式,并使用可重用的解决方案来解决它们。因此,需要讨论微服务的设计模式。在深入研究设计模式之前,我们需要了解构建微服务架构的原则:
- 可扩展性
- 可用性
- 弹性
- 独立自主
- 去中心化治理
- 故障隔离
- 自动配置
- 通过开发运营进行持续交付
应用所有这些原则会带来一些挑战和问题。让我们讨论这些问题及其解决方案。
1. 分解模式
a.按业务能力分解
问题
微服务就是要使服务松散耦合,应用单一责任原则。但是,必须从逻辑上将应用程序分解为更小的部分。如何将应用程序分解为小型服务?
解决方案
一种策略是按业务能力分解。业务能力是企业为了创造价值而做的事情。给定业务的功能集取决于业务类型。例如,保险公司的能力通常包括销售、营销、承保、索赔处理、计费、合规等。每个业务功能都可以被视为一种服务,因为它是面向业务的而不是技术性的。
b.按子域分解
问题
使用业务功能分解应用程序可能是一个好的开始,但你会遇到所谓的“上帝类”,这并不容易分解。这些类在多个服务中是通用的。例如,Order 类将用于订单管理、订单接受、订单交付等。我们如何分解它们?
解决方案
对于“上帝类”问题,DDD(领域驱动设计)来拯救。它使用子域和边界上下文概念来解决此问题。DDD 将为企业创建的整个域模型分解为子域。每个子域都有一个若干个模型,这些模型的范围将称为边界上下文。每个微服务都将围绕边界上下文进行开发。
注意:识别子域并非易事。它需要对业务的理解。与业务能力一样,子域是通过分析业务及其组织结构并确定不同的专业领域来识别的。
c. 扼杀者模式
问题
到目前为止,我们讨论的设计模式是分解“绿地”的应用程序,但我们所做的 80% 的工作都是“棕地”应用程序,这些应用程序是大型的整体应用程序。将上述所有设计模式应用于它们将很困难,因为在实时使用的同时将它们分解成更小的部分是一项艰巨的任务。
注:
Greenfield-绿地指的是在全新环境中从头开发的软件项目;Brownfield-棕地指的是在遗留系统之上开发和部署新的软件系统,或者需要与已经在使用的其他软件共存。
解决方案
出现这个问题时,我们可以采用扼杀者模式来救援。扼杀者原意基于对藤蔓的类比,藤蔓勒死了它所缠绕的树。此解决方案适用于 Web 应用程序,其中调用来回切换,对于每个 URI 调用,可以将服务分解为不同的域并作为单独的服务托管。这个想法是一次做一个域。这将创建两个并排位于同一 URI 空间中的单独应用程序。最终,新重构的应用程序会“扼杀”或替换原始应用程序,直到最终您可以关闭整体式应用程序。
2. 集成模式
a. API 网关模式
问题
当应用程序分解为较小的微服务时,需要解决一些问题:
-
如何调用多个微服务来抽象生产者信息。
-
在不同的渠道(如桌面设备、移动设备和平板电脑)上UI 可能不同,因此在相同的后端服务的支持下,应用需要提供不同的数据来响应这些差异化的请求。
-
不同的使用者可能需要来自可重用微服务的不同格式的响应。谁将提供这类数据转换或字段变化操作?
-
如何处理不同类型的协议,其中一些协议可能不受生产者微服务支持。
解决方案
API 网关有助于解决微服务实现带来的许多问题,不仅限于上述问题。
-
API 网关是任何微服务调用的单一入口点。
-
它可以用作代理服务,将请求路由到相关的微服务,从而抽象出生产者的详细信息。
-
它可以将请求扇出到多个服务,并聚合结果以发送回使用者。
-
一刀切的 API 无法解决消费者的所有需求;此解决方案可以为每个特定类型的客户端创建细粒度 API。
-
它还可以将协议请求(例如AMQP)转换为另一个协议(例如HTTP),反之亦然,以便生产者和消费者可以处理它。
-
它还可以卸载微服务的身份验证/授权责任。
b. 聚合器模式
问题
在上一个模式中,我们利用API 网关模式已经讨论了并使用简易的手段解决了聚合数据部分问题。下面,我们将在这里全面讨论它。将业务功能分解为几个较小的逻辑代码段时,有必要考虑如何协作每个服务返回的数据。此责任不能留给使用者,因为这样它可能需要了解生产者应用程序的内部实现。
解决方案
聚合器模式有助于解决此问题。它讨论了我们如何聚合来自不同服务的数据,然后将最终响应发送给消费者。这可以通过两种方式完成:
1. 复合微服务将调用所有需要的微服务,整合数据,并在发送回之前转换数据。
2. API 网关还可以将请求分区到多个微服务,并在将数据发送给消费者之前聚合数据。
如果要应用任何业务逻辑,建议选择复合微服务。否则,应该在API 网关层提供无逻辑的简易解决方案。
c. 客户端 UI 组合模式
问题
当通过分解业务功能/子域来开发服务时,负责用户体验的服务必须从多个微服务中提取数据。在整体式世界中,过去只有一次从 UI 到后端服务的调用来检索所有数据并刷新/提交 UI 页面。但是,现在就不一样了。我们需要了解如何做到这一点。
解决方案
对于微服务,UI 必须设计为具有屏幕/页面的多个部分/区域的框架。每个部分将调用单个后端微服务来拉取数据。这称为编写特定于服务的 UI 组件。像AngularJS和ReactJS这样的框架有助于轻松做到这一点。这些屏幕称为单页应用程序 (SPA)。这使应用能够刷新屏幕的特定区域,而不是整个页面。
3. 数据库模式
a.每个服务的独享一个数据库
问题
存在如何为微服务定义数据库体系结构的问题。以下是需要解决的问题:
1. 服务必须松散耦合。它们可以独立开发、部署和扩展。
2. 业务事务可能会有实施跨多个服务的需求。
3. 部分业务交易需要查询多个服务拥有的数据。
4. 有时必须复制和分片数据库才能扩展。
5. 不同的业务有不同的数据存储要求。
解决方案
为了解决上述问题,必须为每个微服务设计一个数据库;它必须仅是该服务专用的。它只能由微服务 API 访问。其他服务无法直接访问它。例如,对于关系数据库,我们可以使每个服务的表、服务的模式或每个服务的数据库服务器私有化。每个微服务都应该有一个单独的数据库 ID,以便可以授予单独的访问权限来设置障碍,并阻止其他服务使用私有表。
b. 每个服务的共享数据库
问题
我们已经讨论过每个服务一个数据库是微服务的理想选择,但是当应用程序是绿地并使用DDD开发时,这是可能的。但是如果旧的应用程序是巨大单体结构;并且我们试图想通过引入微服务来改造它时,规范化就不那么容易了。在这种情况下,合适的架构是什么?
解决方案
可能多个微服务之间共享数据库并不是理想解决方案,但这是这个方案却是一个行之有效解决方案。大多数人认为这是微服务的反模式,但对于棕地应用程序,这是将应用程序分解为更小逻辑部分的良好开端。需要切记的事情是这个方法不应应用于绿地应用程序开发过程中。在此模式中,一个数据库可以与多个微服务保持一致,但必须将其限制为最多 2-3 个,否则缩放、自治和独立性将难以执行。
c. 命令查询责任分离 (CQRS)
问题
一旦我们实现了每个服务的数据库,就需要查询,这需要来自多个服务的联合数据 - 这是不可能的。那么,我们如何在微服务架构中实现查询呢?
解决方案
CQRS 建议将应用程序拆分为两部分 — 命令端和查询端。命令端处理创建、更新和删除请求。查询端使用实例化视图处理查询部分。事件溯源模式通常与它一起使用,为任何数据更改创建事件。具体化视图通过订阅事件流来保持更新。
d.Saga 模式
问题
当每个服务都有自己的数据库并且业务事务跨多个服务时,我们如何确保跨服务的数据一致性?例如,对于采用客户信用度的电子商务应用程序来说,该应用程序必须确保新订单不会超过客户的信用额度。由于订单和客户位于不同的数据库中,因此应用程序不能简单地使用本地 ACID 事务。
解决方案
Saga 表示由多个子请求组成的高级业务流程,每个子请求更新单个服务中的数据。每个请求都有一个补偿请求,该请求在请求失败时执行。它可以通过两种方式实现:
-
编排 — 当没有中央协调时,每个服务生成并侦听另一个服务的事件,并决定是否应采取行动。
-
业务流程 — 业务流程协调程序(对象)负责传递的决策制定和业务逻辑排序。
4. 可观测性模式
a. 日志聚合
问题
考虑一个用例,其中应用程序由在多台计算机上运行的多个服务实例组成。请求通常跨越多个服务实例。每个服务实例都以标准化格式生成一个日志文件。如何通过特定请求的日志了解应用程序行为?
解决方案
我们需要一个集中式日志记录服务来聚合来自每个服务实例的日志。用户可以搜索和分析日志。他们可以配置在日志中出现某些消息时触发的警报。例如,PCF确实有Loggeregator,它从PCF平台的每个组件(路由器,控制器等)以及应用程序收集日志。比如在AWS上有 Cloud Watch 就是做这类事情的组件。
b. 绩效指标
问题
当服务组合因微服务架构而增加时,密切关注事务变得至关重要,以便可以监视模式并在出现问题时发送警报。我们应该如何收集指标来监控应用程序性能?
解决方案
需要指标服务来收集有关单个操作的统计信息。它应聚合提供报告和警报的应用程序服务的指标。有两种用于聚合指标的模型:
-
推送 — 服务将指标推送到指标服务,例如NewRelic,AppDynamics
-
拉取 — 指标服务从服务中提取指标,例如Prometheus-普罗米修斯
c. 分布式跟踪
问题
在微服务架构中,请求通常跨越多个服务。每个服务通过跨多个服务执行一个或多个操作来处理请求。那么,我们如何端到端地跟踪请求以解决问题?
解决方案
我们需要一项服务
-
为每个外部请求分配一个唯一的外部请求 ID。
-
将外部请求 ID 传递给所有服务。
-
在所有日志消息中包含外部请求 ID。
-
记录有关在集中式服务中处理外部请求时执行的请求和操作的信息(例如开始时间、结束时间)。
Spring Cloud Slueth和Zipkin服务器是一个常见的实现。
d. 健康检查
问题
实现微服务架构后,服务可能已启动但无法处理事务。在这种情况下,如何确保请求不会发送到这些失败的实例?使用负载平衡模式实现。
解决方案
每个服务都需要有一个可用于检查应用程序运行状况的终结点,例如 。此 API 应检查主机的状态、与其他服务/基础结构的连接以及任何特定逻辑。/health
Spring Boot Actuator 实现了 /health 端点,并且该实现也可以自定义。
5. 跨领域关注模式
a. 外部配置
问题
服务通常也会调用其他服务和数据库。对于每个环境(如DEV, SIT, UAT, PROD),终结点 URL 或某些配置属性可能不同。任何这些属性的更改都可能需要重新生成和重新部署服务。如何避免因配置更改而修改代码?
解决方案
外部化所有配置,包括终结点 URL 和凭据。应用程序应在启动时或动态加载它们。
Spring Cloud 配置服务器提供了将属性外部化到 GitHub 并将它们作为环境属性加载的选项。应用程序可以在启动时访问这些内容,也可以在不重新启动服务器的情况下刷新这些内容。
b. 服务发现模式
问题
当微服务出现时,我们需要解决调用服务方面的几个问题:
-
使用容器技术,IP 地址动态分配给服务实例。每次地址更改时,使用者服务都可能中断,需要手动更改。
-
每个服务 URL 都必须被使用者记住并紧密耦合。
那么,消费者或路由器如何知道所有可用的服务实例和位置呢?
解决方案
需要创建一个服务注册表,用于保存每个生产者服务的元数据。服务实例应在启动时注册到注册表,并在关闭时取消注册。使用者或路由器应查询注册表并找出服务的位置。注册表还需要对创建者服务进行运行状况检查,以确保只有服务的工作实例才能通过它使用。有两种类型的服务发现:客户端和服务器端。客户端发现的一个示例是 Netflix Eureka,服务器端发现的一个例子是 AWS ALB。
c. 断路器模式
问题
服务通常调用其他服务来检索数据,并且下游服务可能会关闭。这有两个问题:首先,请求将继续进入关闭服务,耗尽网络资源并降低性能。其次,用户体验将是糟糕的和不可预测的。如何避免级联服务故障并优雅地处理故障?
解决方案
使用者应通过行为类似于电气断路器的代理调用远程服务。当连续失败次数超过阈值时,断路器将跳闸,并且在超时期限内,所有调用远程服务的尝试都将立即失败。超时到期后,断路器允许有限数量的测试请求通过。如果这些请求成功,断路器将恢复正常运行。否则,如果出现故障,超时期限将再次开始。
Netflix Hystrix是断路器模式的一个很好的实现。它还可以帮助您定义在断路器跳闸时可以使用的回退机制。这提供了更好的用户体验。
d. 蓝绿部署模式
问题
使用微服务架构,一个应用程序可以有多个微服务。如果我们停止所有服务,然后部署增强版本,停机时间将是巨大的,并可能影响业务。此外,回滚将是一场噩梦。我们如何避免或减少部署期间服务的停机时间?
解决方案
可以实施蓝绿部署策略以减少或消除停机时间。它通过运行两个相同的生产环境(蓝色和绿色)来实现这一点。假设绿色是现有的活动实例,蓝色是应用程序的新版本。在任何时候,只有一个环境是实时的,实时环境为所有生产流量提供服务。所有云平台都提供实施蓝绿部署的选项。
微服务架构还使用了许多其他模式,例如 Sidecar、链式微服务、分支微服务、事件溯源模式、持续交付模式等。随着我们获得更多微服务经验,该列表不断增长。