乐趣区

关于微服务:微服务最重要的10个设计模式

大家好,我是不才陈某~

从软件开发晚期(1960 年代)开始,应答大型软件系统中的复杂性始终是一项令人生畏的工作。多年来为了应答软件系统的复杂性,软件工程师和架构师们做了许多尝试:David Parnas 的模块化和封装(1972),Edsger W. Dijkstra(1974)的关注点拆散以及 SOA(1988)

他们都是应用分而治之这项成熟的传统技术来应答大型零碎的复杂性。自 2010 年开始,这些技术被证实无奈持续应答 Web 级利用或者古代大型企业级利用的复杂性。因而架构师和工程师们倒退出了一种全新的古代形式来解决这个问题,就是微服务架构。它尽管连续了分而治之的思维,但却是以全新的形式来实现的。

软件设计模式是解决软件设计中常见问题的通用、可复用的解决方案。设计模式让咱们能够分享通用词汇并应用经实战测验的计划,免得反复造轮子。

通过浏览这篇文章,你会学到:

  • 微服务架构。
  • 微服务架构的劣势。
  • 微服务架构的劣势。
  • 何时应用微服务架构。
  • 最重要的微服务架构设计模式,包含其优缺点、用例、上下文、技术栈示例及可用资源。

请留神,本清单中的大部分设计模式常呈现在多种语境中,并且能够在非微服务架构中应用。而我将在微服务这个特定语境中介绍它们。

微服务架构

那到底什么是微服务架构?有很多种定义方法。我的定义是这这样的:

微服务架构指的是将大型简单零碎按性能或者业务需要垂直切分成更小的子系统,这些子系统以独立部署的子过程存在,它们之间通过轻量级的、跨语言的同步(比方 REST,gRPC)或者异步(音讯)网络调用进行通信。

上面是基于微服务架构的商业 Web 利用的组件视图:

微服务架构的重要特色:

  • 整个应用程序被拆分成互相独立但蕴含多个外部模块的子过程。
  • 与模块化的单体利用(Modular Monoliths)或 SOA 相同,微服务应用程序依据业务范围或畛域垂直拆分。
  • 微服务边界是内部的,微服务之间通过网络调用(RPC 或音讯)互相通信。
  • 微服务是独立的过程,它们能够独立部署。
  • 它们以轻量级的形式进行通信,不须要任何智能通信通道。

微服务架构的长处:

  • 更好的开发规模。
  • 更快的开发速度。
  • 反对迭代开发或现代化增量开发。
  • 充分利用古代软件开发生态系统的劣势(云、容器、DevOps、Serverless)。
  • 反对程度缩放和细粒度缩放。
  • 小体量,较低了开发人员的认知复杂性。

微服务架构的毛病:

  • 更高数量级的流动组件(服务、数据库、过程、容器、框架)。
  • 复杂性从代码转移到基础设施。
  • RPC 调用和网络通信的大量减少。
  • 整个零碎的安全性治理更具备挑战性。
  • 整个零碎的设计变得更加艰难。
  • 引入了分布式系统的复杂性。

何时应用微服务架构:

  • 大规模 Web 利用开发。
  • 跨团队企业级利用合作开发。
  • 长期收益优先于短期收益。
  • 团队领有可能设计微服务架构的软件架构师或高级工程师。

微服务架构的设计模式

1. 独享数据库(Database per Microservice)

当一家公司将大型单体零碎替换成一组微服务,首先要面临的最重要决策是对于数据库。单体架构会应用大型地方数据库。即便转移到微服务架构许多架构师仍偏向于放弃数据库不变。尽管有一些短期收益,但它却是反模式的,特地是在大规模零碎中,微服务将在数据库层重大耦合,整个迁徙到微服务的指标都将面临失败(例如,团队受权、独立开发等问题)。

更好的办法是为每个微服务提供本人的数据存储,这样服务之间在数据库层就不存在强耦合。这里我应用数据库这一术语来示意逻辑上的数据隔离,也就是说微服务能够共享物理数据库,但应该应用离开的数据结构、汇合或者表,这还将有助于确保微服务是依照畛域驱动设计的办法正确拆分的。

长处

  • 数据由服务齐全所有。
  • 服务的开发团队之间耦合度升高。

毛病

  • 服务间的数据共享变得更有挑战性。
  • 在利用范畴的保障 ACID 事务变得艰难许多。
  • 仔细设计如何拆分单体数据库是一项极具挑战的工作。

何时应用独享数据库

  • 在大型企业应用程序中。
  • 当团队须要齐全把控微服务以实现开发规模扩大和速度晋升。

何时不宜应用独享数据库

  • 在小规模利用中。
  • 如果是单个团队开发所有微服务。

可用技术示例

所有 SQL、NoSQL 数据库都提供数据的逻辑拆散(例如,独自的表、汇合、构造、数据库)。

2. 事件源(Event Sourcing)

在微服务架构中,特地应用独享数据库时,微服务之间须要进行数据交换。对于弹性高可伸缩的和可容错的零碎,它们应该通过替换事件进行异步通信。在这种状况,您可能心愿进行相似更新数据库并发送音讯这样的原子操作,如果在大数据量的分布式场景应用关系数据库,您将无奈应用两阶段锁协定(2PL),因为它无奈伸缩。而 NoSQL 数据库因为大多不反对两阶段锁协定甚至无奈实现分布式事务。

在这些场景,能够基于事件的架构应用事件源模式。在传统数据库中,间接存储的是业务实体的以后“状态”,而在事件源中任何“状态”更新事件或其余重要事件都会被存储起来,而不是间接存储实体自身。这意味着业务实体的所有更改将被保留为一系列不可变的事件。因为数据是作为一系列事件存储的,而非间接更新存储,所以各项服务能够通过重放事件存储中的事件来计算出所需的数据状态。

长处

  • 为高可伸缩零碎提供原子性操作。
  • 自动记录实体变更历史,包含时序回溯性能。
  • 松耦合和事件驱动的微服务。

毛病

  • 从事件存储中读取实体成为新的挑战,通常须要额定的数据存储(CQRS 模式)。
  • 零碎整体复杂性减少了,通常须要畛域驱动设计。
  • 零碎须要处理事件反复(幂等)或失落。
  • 变更事件构造成为新的挑战。

何时应用事件源

  • 应用关系数据库的、高可伸缩的事务型零碎。
  • 应用 NoSQL 数据库的事务型零碎。
  • 弹性高可伸缩微服务架构。
  • 典型的音讯驱动或事件驱动零碎(电子商务、预订和预约零碎)。

何时不宜应用事件源

  • 应用 SQL 数据库的低可伸缩性事务型零碎
  • 在服务能够同步替换数据(例如,通过 API)的简略微服务架构中。

可用技术示例

事件存储:EventStoreDB,Apache Kafka,Confluent Cloud,AWS Kinesis,Azure Event Hub,GCP Pub/Sub,Azure Cosmos DB,MongoDB,Cassandra. Amazon DynamoDB

框架:Lagom,Akka,Spring,akkatecture,Axon,Eventuate

3. 命令和查问职责拆散(CQRS)

如果咱们应用事件源,那么从事件存储中读取数据就变得艰难了。要从数据存储中获取实体,咱们须要解决所有的实体事件。有时咱们对读写操作还会有不同的一致性和吞吐量要求。

这种状况,咱们能够应用 CQRS 模式。在该模式中,零碎的数据批改局部(命令)与数据读取局部(查问)是拆散的。而 CQRS 模式有两种容易令人混同的模式,别离是简略的和高级的。

在其简略模式中,不同实体或 ORM 模型被用于读写操作,如下所示:

它有助于强化繁多职责准则和拆散关注点,从而实现更简洁的设计。

在其高级模式中,会有不同的数据存储用于读写操作。高级的 CQRS 通常联合事件源模式。依据不同状况,会应用不同类型的写数据存储和读数据存储。写数据存储是“记录的零碎”,也就是整个零碎的外围源头。

对于读频繁的应用程序或微服务架构,OLTP 数据库(任何提供 ACID 事务保障的关系或非关系数据库)或分布式音讯零碎都能够被用作写存储。对于写频繁的应用程序(写操作高可伸缩性和大吞吐量),须要应用写可程度伸缩的数据库(如寰球托管的公共云数据库)。标准化的数据则保留在写数据存储中。

对搜寻(例如 Apache Solr、Elasticsearch)或读操作(KV 数据库、文档数据库)进行优化的非关系数据库常被用作读存储。许多状况会在须要 SQL 查问的中央应用读可伸缩的关系数据库。非标准化和非凡优化过的数据则保留在读存储中。

数据是从写存储异步复制到读存储中的,所以读存储和写存储之间会有提早,但最终是统一的。

长处

  • 在事件驱动的微服务中数据读取速度更快。
  • 数据的高可用性。
  • 读写零碎可独立扩大。

毛病

  • 读数据存储是弱一致性的(最终一致性)。
  • 整个零碎的复杂性减少了,凌乱的 CQRS 会显着危害整个我的项目。

何时应用 CQRS

  • 在高可扩大的微服务架构中应用事件源。
  • 在简单畛域模型中,读操作须要同时查问多个数据存储。
  • 在读写操作负载差别显著的零碎中。

何时不宜应用 CQRS

  • 在没有必要存储大量事件的微服务架构中,用事件存储快照来计算实体状态是一个更好的抉择。
  • 在读写操作负载相近的零碎中。

可用技术示例

写存储:EventStoreDB,Apache Kafka,Confluent Cloud,AWS Kinesis,Azure Event Hub,GCP Pub/Sub,Azure Cosmos DB,MongoDB,Cassandra. Amazon DynamoDB

读存储:Elastic Search,Solr,Cloud Spanner,Amazon Aurora,Azure Cosmos DB,Neo4j

框架:Lagom,Akka,Spring,akkatecture,Axon,Eventuate

4. Saga

如果微服务应用独享数据库,那么通过分布式事务管理一致性是一个微小的挑战。你无奈应用传统的两阶段提交协定,因为它要么不可伸缩(关系数据库),要么不被反对(少数非关系数据库)。

但您还是能够在微服务架构中应用 Saga 模式实现分布式事务。Saga 是 1987 年开发的一种古老模式,是关系数据库中对于大事务的一个代替概念。但这种模式的一种古代变种对分布式事务也十分无效。Saga 模式是一个本地事务序列,其每个事务在一个独自的微服务内更新数据存储并公布一个事件或音讯。Saga 中的首个事务是由内部申请(事件或动作)初始化的,一旦本地事务实现(数据已保留在数据存储且音讯或事件已公布),那么公布的音讯或事件则会触发 Saga 中的下一个本地事务。

如果本地事务失败,Saga 将执行一系列弥补事务来回滚后面本地事务的更改。

Saga 事务协调治理次要有两种模式:

  • 事件编排 Choreography:扩散协调,每个微服务生产并监听其余微服务的事件或音讯而后决定是否执行某个动作。
  • 命令编排 Orchestration:集中协调,由一个协调器通知参加的微服务哪个本地事务须要执行。

长处

  • 为高可伸缩或松耦合的、事件驱动的微服务架构提供一致性事务。
  • 为应用了不反对 2PC 的非关系数据库的微服务架构提供一致性事务。

毛病

  • 须要解决刹时故障,并且提供等幂性。
  • 难以调试,而且复杂性随着微服务数量减少而减少。

何时应用 Saga

  • 在应用了事件源的高可伸缩、松耦合的微服务中。
  • 在应用了分布式非关系数据库的零碎中。

何时不宜应用 Saga

  • 应用关系数据库的低可伸缩性事务型零碎。
  • 在服务间存在循环依赖的零碎中。

可用技术示例

Axon,Eventuate,Narayana

5. 面向前端的后端(BFF)

在古代商业利用开发,特地是微服务架构中,前后端利用是拆散和独立的服务,它们通过 API 或 GraphQL 连贯。如果应用程序还有挪动 App 客户端,那么 Web 端和挪动客户端应用雷同的后端微服务就会呈现问题。因为挪动客户端和 Web 客户端有不同的屏幕尺寸、显示屏、性能、能耗和网络带宽,它们的 API 需要不同。

面向前端的后端模式实用于须要为非凡 UI 定制独自后端的场景。它还提供了其余劣势,比方作为上游微服务的封装,从而缩小 UI 和上游微服务之间的频繁通信。此外,在高平安要求的场景中,BFF 为部署在 DMZ 网络中的上游微服务提供了更高的安全性。

长处

  • 拆散 BFF 之间的关注点,使得咱们能够为具体的 UI 优化他们。
  • 提供更高的安全性。
  • 缩小 UI 和上游微服务之间频繁的通信。

毛病

  • BFF 之间代码反复。
  • 大量的 BFF 用于其余用户界面(例如,智能电视,Web,挪动端,PC 桌面版)。
  • 须要认真的设计和实现,BFF 不应该蕴含任何业务逻辑,而应只蕴含特定客户端逻辑和行为。

何时应用 BFF

  • 如果应用程序有多个含不同 API 需要的 UI。
  • 出于平安须要,UI 和上游微服务之间须要额定的层。
  • 如果在 UI 开发中应用微前端。

何时不宜应用 BFF

  • 如果应用程序虽有多个 UI,但应用的 API 雷同。
  • 如果外围微服务不是部署在 DMZ 网络中。

可用技术示例

任何后端框架(Node.js,Spring,Django,Laravel,Flask,Play,…)都能反对。

6. API 网关

在微服务架构中,UI 通常连贯多个微服务。如果微服务是细粒度的(FaaS),那么客户端可能须要连贯十分多的微服务,这将变得繁冗和具备挑战性。此外,这些服务包含它们的 API 还将一直进化。大型企业还心愿能有其余横切关注点(SSL 终止、身份验证、受权、节流、日志记录等)。

一个解决这些问题的可行办法是应用 API 网关。API 网关位于客户端 APP 和后端微服务之间充当 facade,它能够是反向代理,将客户端申请路由到适当的后端微服务。它还反对将客户端申请扇出到多个微服务,而后将响应聚合后返回给客户端。它还反对必要的横切关注点。

长处

  • 在前端和后端服务之间提供松耦合。
  • 缩小客户端和微服务之间的调用次数。
  • 通过 SSL 终端、身份验证和受权实现高安全性。
  • 集中管理的横切关注点,例如,日志记录和监督、节流、负载平衡。

毛病

  • 可能导致微服务架构中的单点故障。
  • 额定的网络调用带来的提早减少。
  • 如果不进行扩大,它们很容易成为整个企业应用的瓶颈。
  • 额定的保护和开发费用。

何时应用 API 网关

  • 在简单的微服务架构中,它简直是必须的。
  • 在大型企业中,API 网关是中心化安全性和横切关注点的必要工具。

何时不宜应用 API 网关

  • 在平安和集中管理不是最优先因素的私人我的项目或小公司中。
  • 如果微服务的数量相当少。

可用技术示例

Amazon API 网关,Azure API 治理,Apigee,Kong,WSO2 API 管理器

7. Strangler

如果想在运行中的我的项目中应用微服务架构,咱们须要将遗留的或现有的单体利用迁徙到微服务。将现有的大型在线单体应用程序迁徙到微服务是相当有挑战性的,因为这可能毁坏应用程序的可用性。

一个解决方案是应用 Strangler 模式。Strangler 模式意味着通过应用新的微服务逐渐替换特定性能,将单体应用程序增量地迁徙到微服务架构。此外,新性能只在微服务中增加,而不再增加到遗留的单体利用中。而后配置一个 Facade(API 网关)来路由遗留单体利用和微服务间的申请。当某个性能从单体利用迁徙到微服务,Facade 就会拦挡客户端申请并路由到新的微服务。一旦迁徙了所有的性能,遗留单体应用程序就会被“扼杀(Strangler)”,即服役。

长处

  • 平安的迁徙单体应用程序到微服务。
  • 能够并行地迁徙已有性能和开发新性能。
  • 迁徙过程能够更好把控节奏。

毛病

  • 在现有的单体应用服务和新的微服务之间共享数据存储变得具备挑战性。
  • 增加 Facade(API 网关)将减少零碎提早。
  • 端到端测试变得艰难。

何时应用 Strangler

将大型后端单体应用程序的增量迁徙到微服务。

何时不宜应用 Strangler

  • 如果后端单体利用很小,那么全量替换会更好。
  • 如果无奈拦挡客户端对遗留的单体应用程序的申请。

可用技术示例子

API 网关后端利用框架。

8. 断路器

在微服务架构中,微服务通过同步调用其余服务来满足业务需要。服务调用会因为刹时故障(网络连接迟缓、超时或临时不可用)导致失败,这种状况重试能够解决问题。然而,如果呈现了重大问题(微服务齐全失败),那么微服务将长时间不可用,这时重试没有意义且节约贵重的资源(线程被阻塞,CPU 周期被节约)。此外,一个服务的故障还会引发整个利用零碎的级联故障。这时疾速失败是一种更好的办法。

在这种状况,能够应用断路器模式解救。一个微服务通过代理申请另一个微服务,其工作原理相似于电气断路器,代理通过统计最近产生的故障数量,并应用它来决定是持续申请还是简略的间接返回异样。

断路器能够有以下三种状态:

  • 敞开:断路器将申请路由到微服务,并统计给定时段内的故障数量,如果超过阈值,它就会触发并进入关上状态。
  • 关上:来自微服务的申请会疾速失败并返回异样。在超时后,断路器进入半开启状态。
  • 半开:只有无限数量的微服务申请被容许通过并进行调用。如果这些申请胜利,断路器将进入闭合状态。如果任何申请失败,断路器则会进入开启状态。

长处

  • 进步微服务架构的容错性和弹性。
  • 阻止引发其余微服务的级联故障。

毛病

  • 须要简单的异样解决。
  • 日志和监控。
  • 应该反对人工复位。

何时应用断路器

  • 在微服务间应用同步通信的紧耦合的微服务架构中。
  • 如果微服务依赖多个其余微服务。

何时不宜应用断路器

  • 松耦合、事件驱动的微服务架构。
  • 如果微服务不依赖于其余微服务。

可用技术示例

API 网关,服务网格,各种断路器库(Hystrix,Reselience4J,Polly)。

9. 内部化配置

每个业务利用都有许多用于各种基础设施的配置参数(例如,数据库、网络、连贯的服务地址、凭据、证书门路)。此外在企业应用程序通常部署在各种运行环境(Local、Dev、Prod)中,实现这些的一个办法是通过外部配置。这是一个致命蹩脚实际,它会导致重大的平安危险,因为生产凭证很容易受到毁坏。此外,配置参数的任何更改都须要从新构建应用程序,这在在微服务架构中会更加严厉,因为咱们可能领有数百个服务。

更好的办法是将所有配置内部化,使得构建过程与运行环境拆散,生产的配置文件只在运行时或通过环境变量应用,从而最小化了平安危险。

长处

  • 生产配置不属于代码库,因此最小化了安全漏洞。
  • 批改配置参数不须要从新构建应用程序。

毛病

  • 咱们须要抉择一个反对内部化配置的框架。
  • 何时应用内部化配置
  • 任何重要的生产应用程序都必须应用内部化配置。
  • 何时不宜应用内部化配置
  • 在验证概念的开发中。

可用技术示例

简直所有企业级的古代框架都反对内部化配置。

10. 生产端驱动的契约测试

在微服务架构中,通常有许多有不同团队开发的微服务。这些微型服务协同工作来满足业务需要(例如,客户申请),并互相进行同步或异步通信。生产端微服务的集成测试具备挑战性,通常用 TestDouble 以取得更快、更低成本的测试运行。然而 TestDouble 通常并不能代表真正的微服务提供者,而且如果微服务提供者更改了它的 API 或 音讯,那么 TestDouble 将无奈确认这些。另一种抉择是进行端到端测试,只管它在生产之前是强制性的,但却是软弱的、迟缓的、低廉的且不能代替集成测试(Test Pyramid)。

在这方面生产端驱动的契约测试能够帮忙咱们。在这里,负责生产端微服务的团队针对特定的服务端微服务,编写一套蕴含了其申请和预期响应(同步)或音讯(异步)的测试套件,这些测试套件称为显式的约定。对于微服务服务端,将其生产端所有约定的测试套件都增加到其自动化测试中。当特定服务端微服务的自动化测试执行时,它将一起运行本人的测试和约定的测试并进行验证。通过这种形式,契约测试能够主动的帮忙保护微服务通信的完整性。

长处

  • 如果提供程序意外更改 API 或音讯,能够被疾速的主动发现。
  • 更少意外、更强壮,特地是蕴含大量微服务的企业应用程序。
  • 改善团队自主性。

毛病

  • 须要额定的工作来开发和集成微服务服务端的契约测试,因为他们可能应用齐全不同的测试工具。
  • 如果契约测试与实在服务状况不匹配,将可能导致生产故障。

何时应用需要驱动的契约测试

在大型企业业务应用程序中,通常由不同的团队开发不同服务。

何时不宜应用生产端驱动的契约测试

  • 所有微服务由同一团队负责开发的小型简略的应用程序。
  • 如果服务端微服务是绝对稳固的,并且不处在沉闷的开发状态。

可用技术示例

Pact,Postman,Spring Cloud Contract

总 结

在古代大规模企业软件开发中,微服务架构可能帮忙开发扩大规模并带来很多长期收益。然而微服务架构并不是随处可用的银弹,如果利用在谬误的应用程序类型,微服务架构将弊大于利。心愿采纳微服务架构的开发团队应该遵循最佳实际,并应用一系列可重用的、久经锻炼的设计模式。

微服务架构中至关重要的设计模式是独享数据库。实现这种设计模式具备挑战性,须要其余几种密切相关的设计模式(事件驱动、CQRS、Saga)来反对。在具备多个客户端(Web、Mobile、Desktop、Smart Devices)的典型业务应用程序中,客户端和微服务之间的通信量可能是很大的,并且须要对立的安全控制,在这种状况面向前端的后端和 API 网关的设计十分有用。此外,断路器模式能够大大地帮忙应答这类应用程序的错误处理场景。迁徙遗留的单体利用到微服务是极具挑战性的,而 Strangler 模式能够帮忙做到这点。生产端驱动的契约测试是微服务集成测试的根底模式。另外内部化配置是任何现代化利用程序开发中的一种必备模式。

这个系列并不全面,在理论状况中您可能须要其余的设计模式,但这个系列能为您提供一个对于微服务架构设计模式的极好介绍。

退出移动版