阅读 313

[译]微服务的设计模式

了解微服务架构的设计模式以克服采用它所带来的挑战。

微服务架构已经成为现代应用程序开发中公认的技术选择。尽管它解决了某些问题,但不是灵丹妙药。它有几个缺点,使用这种体系架构时,还需要解决许多问题。这就需要学习这些问题的通用模式,并通过可重用的解决方案来解决它们。因此,有必要讨论微服务的设计模式。在深入研究设计模式之前,我们需要了解微服务架构的构建原理: 1.可扩展性 2.可用性 3.弹性 4.独立自治性 5.去中心化治理 6.失败隔离 7.自动配置 8.通过DevOps持续交付

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


分解模式

按业务能力分解

问题

微服务就是要应用单一责任原则使服务松散耦合。但是,将应用程序分成较小的部分必须在逻辑上完成。那么,我们如何将应用程序分解为小型服务呢?

解决

一种策略是按业务能力分解。所谓业务能力就是可以体现企业业务价值的,给定业务的功能集取决于业务类型。例如,保险公司的功能通常包括销售,市场营销,承保,理赔处理,计费,合规性等。每个业务功能都可以视为一种服务,但它是面向业务的,而不是技术的。

按子域分解

问题

使用业务功能分解应用程序可能是一个不错的开始,但是您会遇到所谓的“神类”,这些类将不容易分解。它们将在多种服务中通用。例如,订单类将用于“订单管理”,“接单”,“订单交付”等业务中。我们该如何分解它们?

解决

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

扼杀模式

问题

到目前为止,我们讨论的设计模式都是分解未开发的应用程序,但是我们所做的工作中有80%是用于棕色的应用程序,这是大型的整体应用程序。将上述所有设计模式应用于它们将很困难,因为把他们当作一个整体应用的同时将它们拆分成一个个较小的部分是一项艰巨的任务。

解决

扼杀者模式可以解决此类问题。扼杀者模式是基于藤蔓缠绕植物的类比。此解决方案与Web应用程序配合使用,在Web应用程序之间来回调用,对于每个URI调用,服务可以分为不同的域并作为单独的服务托管。这个想法是一次做一个域。这将创建两个单独的应用程序,它们在同一URI空间中并排运行。最终,新重构的应用程序会“扼杀”或替换原始应用程序,直到最终您可以关闭整体应用程序。

整合模式

api网关

问题

当一个应用被分解为多个微服务时,还会有一些问题需要解决。

1.如何调用多个微服务来抽象生产者信息。

2.在不同的渠道(例如台式机,移动设备和平板电脑)上,由于UI可能不同,应用程序需要不同的数据来响应相同的后端服务。

3.不同的使用者对于可重复使用的微服务响应格式可能不同。谁将进行数据转换或现场操作?

4.生产者微服务可能不支持某些类型协议的处理方式。

解决

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

1.API网关是任何微服务调用的单一入口点。

2.它可以用作代理服务,以将请求路由到相关的微服务,从而抽象出生产者详细信息。

3.它可以将对多个服务的请求散发出去,然后汇总结果以发送回消费者。

4.千篇一律的所有API不能解决所有消费者的需求;该解决方案可以为每种特定类型的客户端创建一个细粒度的API。

5.它还可以将协议请求(例如AMQP)转换为另一个协议(例如HTTP),反之亦然,以便生产者和消费者可以处理它。

6.它还可以减轻微服务的身份验证/授权责任。

聚合器

问题

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

解决

聚集器模式有助于解决此问题。它讨论了如何聚合来自不同服务的数据,然后将最终响应发送给消费者。这可以通过两种方式完成:

1.复合微服务将调用所有必需的微服务,合并数据,并在发送回数据之前对其进行转换。

2.API网关还可以将请求划分为多个微服务并聚合数据,然后再将其发送给使用者。

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

客户端UI组合

问题

通过分解业务功能/子域来开发服务时,负责用户体验的服务必须从多个微服务中提取数据。在整体应用中,从UI到后端服务只有一次调用,以检索所有数据并刷新/提交UI页面。但是,现在不一样了。我们需要了解如何去做。

解决

对于微服务,必须将UI设计为具有屏幕/页面的多个部分/区域的框架。每个部分都将调用单个后端微服务以提取数据。这称为组成特定于服务的UI组件。诸如AngularJS和ReactJS之类的框架可以轻松地做到这一点。这些屏幕称为单页应用程序(SPA)。这使应用程序可以刷新屏幕的特定区域而不是刷新整个页面。

数据库模式

每个服务一个数据库

问题

如何定义微服务的数据库体系结构存在一个问题。以下是要解决的问题:

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

2.业务事务可能会强制跨越多个服务的不变量。

3.一些业务事务需要查询多个服务拥有的数据。

4.有时必须复制数据库并对其进行分片以进行扩展。

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

解决

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

每个服务共享数据库

问题

我们已经讨论了每个服务一个数据库是微服务的理想选择,当应用程序是未开发的并且要使用DDD开发时,这是可能的。但是,如果应用程序是一个整体并且试图闯入微服务,那么非规范化就不那么容易了。在那种情况下合适的架构是什么?

解决

每个服务共享数据库不是理想的选择,但这是上述情况的可行解决方案。大多数人认为这是微服务的反模式,但对于棕色应用程序来说,这是将应用程序分解成较小逻辑部分的一个很好的开始。这不适用于未开发的应用程序。在这种模式下,一个数据库可以与一个以上的微服务对齐,但是必须限制为最大2-3个微服务,否则伸缩,自治和独立性将难以执行。

命令查询职责隔离(CQRS)

问题

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

解决

CQRS建议将应用程序分为两部分-命令端和查询端。命令行处理创建,更新和删除请求。查询端通过使用实例化视图来处理查询部分。通常将事件源模式与它一起使用来为任何数据更改创建事件。通过订阅事件流,可以使实例化视图保持更新。

saga模式

问题

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

解决

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

1.Choreography-如果没有中央协调,则每个服务都会产生并侦听另一个服务的事件,并决定是否应采取措施。 2.Orchestration-协调员(对象)负责传奇的决策和业务逻辑排序。

可观察性模式

日志汇总

问题

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

解决

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

性能指标

问题

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

解决

需要度量服务来收集有关单个操作的统计信息。它应该聚合提供报告和警报的应用程序服务的指标。有两种用于汇总指标的模型:

1.推送-服务将指标推送到指标服务,例如NewRelic,AppDynamics

2.提取-指标服务从服务中提取指标,例如普罗米修斯

分布式跟踪

问题

在微服务架构中,请求通常跨越多个服务。每个服务通过跨多个服务执行一个或多个操作来处​​理请求。然后,我们如何跟踪端到端的请求以解决问题?

解决

我们需要一项服务

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

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

💡在所有日志消息中包括外部请求ID。

💡记录有关请求和在集中式服务中处理外部请求时执行的操作的信息(例如开始时间,结束时间)。

Spring Cloud Slueth与Zipkin服务器一起是常见的实现。

健康检查

问题

实施微服务架构后,服务可能会启动但无法处理事务。在这种情况下,如何确保请求不会发送到那些失败的实例?用负载均衡模式实现。

解决

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

Cross-Cutting Concern(横切关注)模式

外部配置

问题

服务通常还会调用其他服务和数据库。对于开发,质量检查,UAT,产品等每个环境,端点URL或某如些配置属性可能会有所不同。这些属性中的任何一个更改都可能需要重新构建和重新部署服务。我们何避免对配置更改进行代码修改?

解决

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

服务发现

问题

当微服务出现时,我们需要在调用服务方面解决一些问题:

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

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

解决

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

断路器

问题

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

解决

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

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

蓝绿部署

问题

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

解决

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


微服务体系结构还有许多其他模式,例如Sidecar,链式微服务,分支微服务,事件源模式,连续交付模式等。随着我们在微服务方面获得更多经验,该列表不断增长。我现在停下来,听听您正在使用什么微服务模式。

⚠️本文原文连接:dzone.com/articles/de…