微服务架构十大架构模式你知道几个?

软件设计模式是解决软件设计中常见问题的通用、可复用的解决方案。设计模式让咱们能够分享通用词汇并使用经实战检验的方案,以避免重复造轮子。如今,我将介绍一系列设计模式来实现这些最佳实践。html

微服务架构的设计模式

独享数据库(Database per Microservice)

当一家公司将大型单体系统替换成一组微服务,首先要面临的最重要决策是关于数据库。单体架构会使用大型中央数据库。即便转移到微服务架构许多架构师仍倾向于保持数据库不变。虽然有一些短时间收益,但它倒是反模式的,特别是在大规模系统中,微服务将在数据库层严重耦合,整个迁移到微服务的目标都将面临失败(例如,团队受权、独立开发等问题)。前端

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

img图片程序员

优势数据库

  • 数据由服务彻底全部。
  • 服务的开发团队之间耦合度下降。

缺点后端

  • 服务间的数据共享变得更有挑战性。
  • 在应用范围的保证 ACID 事务变得困难许多。
  • 细心设计如何拆分单体数据库是一项极具挑战的任务。

什么时候使用独享数据库设计模式

  • 在大型企业应用程序中。
  • 当团队须要彻底把控微服务以实现开发规模扩展和速度提高。

什么时候不宜使用独享数据库api

  • 在小规模应用中。
  • 若是是单个团队开发全部微服务。

可用技术示例安全

全部 SQL、 NoSQL 数据库都提供数据的逻辑分离(例如,单独的表、集合、结构、数据库)。markdown

延伸阅读

微服务模式:独享数据库 microservices.io/patterns/da…

分布式数据存储 docs.microsoft.com/en-us/dotne…

事件源(Event Sourcing)

在微服务架构中,特别使用独享数据库时,微服务之间须要进行数据交换。对于弹性高可伸缩的和可容错的系统,它们应该经过交换事件进行异步通讯。在这种状况,您可能但愿进行相似更新数据库并发送消息这样的原子操做,若是在大数据量的分布式场景使用关系数据库,您将没法使用两阶段锁协议(2PL),由于它没法伸缩。而 NoSQL 数据库由于大多不支持两阶段锁协议甚至没法实现分布式事务。

这里为你们准备了一份小小的礼物,关注公众号【无聊学Java】,输入以下代码,便可得到!
0001:《程序员必读书籍》
0002:《从无到有搭建中小型互联网公司后台服务架构与运维架构》
0003:《互联网企业高并发解决方案》
0004:《Spring Cloud Alibaba实战视频》
0006:《SpringBoot实现点餐系统》
0007:《RabbitMQ最新实战视频》
0008:《Hadoop实战教学视频》
复制代码

在这些场景,能够基于事件的架构使用事件源模式。在传统数据库中,直接存储的是业务实体的当前“状态”,而在事件源中任何“状态”更新事件或其余重要事件都会被存储起来,而不是直接存储实体自己。这意味着业务实体的全部更改将被保存为一系列不可变的事件。由于数据是做为一系列事件存储的,而非直接更新存储,因此各项服务能够经过重放事件存储中的事件来计算出所需的数据状态。

img图片

优势

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

缺点

  • 从事件存储中读取实体成为新的挑战,一般须要额外的数据存储(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

延伸阅读

事件驱动 martinfowler.com/eaaDev/Even…

事件驱动模式-云设计模式 docs.microsoft.com/en-us/azure…

微服务模式:事件驱动 microservices.io/patterns/da…

命令和查询职责分离(CQRS)

若是咱们使用事件源,那么从事件存储中读取数据就变得困难了。要从数据存储中获取实体,咱们须要处理全部的实体事件。有时咱们对读写操做还会有不一样的一致性和吞吐量要求。

这种状况,咱们可使用 CQRS 模式。在该模式中,系统的数据修改部分(命令)与数据读取部分(查询)是分离的。而 CQRS 模式有两种容易使人混淆的模式,分别是简单的和高级的。

在其简单形式中,不一样实体或 ORM 模型被用于读写操做,以下所示:

img图片

它有助于强化单一职责原则和分离关注点,从而实现更简洁的设计。

在其高级形式中,会有不一样的数据存储用于读写操做。高级的 CQRS 一般结合事件源模式。根据不一样状况,会使用不一样类型的写数据存储和读数据存储。写数据存储是“记录的系统”,也就是整个系统的核心源头。

img图片

对于读频繁的应用程序或微服务架构,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

延伸阅读

bliki:CQRS martinfowler.com/bliki/CQRS.…

CQRS模式 - Azure 架构中心 docs.microsoft.com/en-us/azure…

微服务模式:命令和查询职责分离(CQRS) microservices.io/patterns/da…

Saga

若是微服务使用独享数据库,那么经过分布式事务管理一致性是一个巨大的挑战。你没法使用传统的两阶段提交协议,由于它要么不可伸缩(关系数据库),要么不被支持(多数非关系数据库)。

但您仍是能够在微服务架构中使用 Saga 模式实现分布式事务。Saga 是 1987 年开发的一种古老模式,是关系数据库中关于大事务的一个替代概念。但这种模式的一种现代变种对分布式事务也很是有效。Saga 模式是一个本地事务序列,其每一个事务在一个单独的微服务内更新数据存储并发布一个事件或消息。Saga 中的首个事务是由外部请求(事件或动做)初始化的,一旦本地事务完成(数据已保存在数据存储且消息或事件已发布),那么发布的消息或事件则会触发 Saga 中的下一个本地事务。

img图片

若是本地事务失败,Saga 将执行一系列补偿事务来回滚前面本地事务的更改。

Saga 事务协调管理主要有两种形式:

  • 事件编排 Choreography:分散协调,每一个微服务生产并监听其余微服务的事件或消息而后决定是否执行某个动做。
  • 命令编排 Orchestration:集中协调,由一个协调器告诉参与的微服务哪一个本地事务须要执行。

优势

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

缺点

  • 须要处理瞬时故障,而且提供等幂性。
  • 难以调试,并且复杂性随着微服务数量增长而增长。

什么时候使用 Saga

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

什么时候不宜使用 Saga

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

可用技术示例

Axon, Eventuate, Narayana

延伸阅读

Saga分布式事务-Azure设计模式 docs.microsoft.com/en-us/azure…

微服务模式:Sagas microservices.io/patterns/da…

Saga 模式:微服务中的应用程序事务 blog.couchbase.com/saga-patter…

面向前端的后端 (BFF)

在现代商业应用开发,特别是微服务架构中,先后端应用是分离和独立的服务,它们经过 API 或 GraphQL 链接。若是应用程序还有移动 App 客户端,那么 Web 端和移动客户端使用相同的后端微服务就会出现问题。由于移动客户端和 Web 客户端有不一样的屏幕尺寸、显示屏、性能、能耗和网络带宽,它们的 API 需求不一样。

面向前端的后端模式适用于须要为特殊 UI 定制单独后端的场景。它还提供了其余优点,好比做为下游微服务的封装,从而减小 UI 和下游微服务之间的频繁通讯。此外,在高安全要求的场景中,BFF 为部署在 DMZ 网络中的下游微服务提供了更高的安全性。

img图片

优势

  • 分离 BFF 之间的关注点,使得咱们能够为具体的 UI 优化他们。
  • 提供更高的安全性。
  • 减小 UI 和下游微服务之间频繁的通讯。

缺点

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

什么时候使用 BFF

  • 若是应用程序有多个含不一样 API 需求的 UI。
  • 出于安全须要,UI 和下游微服务之间须要额外的层。
  • 若是在 UI 开发中使用微前端。

什么时候不宜使用 BFF

  • 若是应用程序虽有多个 UI,但使用的 API 相同。
  • 若是核心微服务不是部署在 DMZ 网络中。

可用技术示例

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

延伸阅读

Sam Newman - 面向前端的后端 samnewman.io/patterns/ar…

面向前端的后端模式 - 云设计模式 docs.microsoft.com/en-us/azure…

微服务模式:API 网关模式 microservices.io/patterns/ap…

API 网关

在微服务架构中,UI 一般链接多个微服务。若是微服务是细粒度的(FaaS) ,那么客户端可能须要链接很是多的微服务,这将变得繁杂和具备挑战性。此外,这些服务包括它们的 API 还将不断进化。大型企业还但愿能有其余横切关注点(SSL 终止、身份验证、受权、节流、日志记录等)。

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

img图片

优势

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

缺点

  • 可能致使微服务架构中的单点故障。
  • 额外的网络调用带来的延迟增长。
  • 若是不进行扩展,它们很容易成为整个企业应用的瓶颈。
  • 额外的维护和开发费用。

什么时候使用 API 网关

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

什么时候不宜使用 API 网关

  • 在安全和集中管理不是最优先要素的私人项目或小公司中。
  • 若是微服务的数量至关少。

可用技术示例

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

延伸阅读

微服务模式:API 网关模式 microservices.io/patterns/ap…

API 网关-Azure 架构中心 docs.microsoft.com/en-us/azure…

Strangler

若是想在运行中的项目中使用微服务架构,咱们须要将遗留的或现有的单体应用迁移到微服务。将现有的大型在线单体应用程序迁移到微服务是至关有挑战性的,由于这可能破坏应用程序的可用性。

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

img

优势

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

缺点

  • 在现有的单体应用服务和新的微服务之间共享数据存储变得具备挑战性。
  • 添加 Facade (API 网关)将增长系统延迟。
  • 端到端测试变得困难。

什么时候使用 Strangler

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

什么时候不宜使用 Strangler

  • 若是后端单体应用很小,那么全量替换会更好。
  • 若是没法拦截客户端对遗留的单体应用程序的请求。

可用技术示例

API 网关后端应用框架。

延伸阅读

bliki:StranglerFig 应用程序 martinfowler.com/bliki/Stran…

Strangler 模式 - 云设计模式 docs.microsoft.com/en-us/azure…

微服务模式:Strangler 应用程序 microservices.io/patterns/re…

断路器

在微服务架构中,微服务经过同步调用其余服务来知足业务需求。服务调用会因为瞬时故障(网络链接缓慢、超时或暂时不可用) 致使失败,这种状况重试能够解决问题。然而,若是出现了严重问题(微服务彻底失败),那么微服务将长时间不可用,这时重试没有意义且浪费宝贵的资源(线程被阻塞,CPU 周期被浪费)。此外,一个服务的故障还会引起整个应用系统的级联故障。这时快速失败是一种更好的方法。

在这种状况,可使用断路器模式挽救。一个微服务经过代理请求另外一个微服务,其工做原理相似于电气断路器,代理经过统计最近发生的故障数量,并使用它来决定是继续请求仍是简单的直接返回异常。

img

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

  • 关闭:断路器将请求路由到微服务,并统计给定时段内的故障数量,若是超过阈值,它就会触发并进入打开状态。
  • 打开:来自微服务的请求会快速失败并返回异常。在超时后,断路器进入半开启状态。
  • 半开:只有有限数量的微服务请求被容许经过并进行调用。若是这些请求成功,断路器将进入闭合状态。若是任何请求失败,断路器则会进入开启状态。

优势

  • 提升微服务架构的容错性和弹性。
  • 阻止引起其余微服务的级联故障。

缺点

  • 须要复杂的异常处理。
  • 日志和监控。
  • 应该支持人工复位。

什么时候使用断路器

  • 在微服务间使用同步通讯的紧耦合的微服务架构中。
  • 若是微服务依赖多个其余微服务。

什么时候不宜使用断路器

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

可用技术示例

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

延伸阅读

bliki:断路器 martinfowler.com/bliki/Circu…

断路器模式 - 云设计模式 docs.microsoft.com/en-us/azure…

微型服务模式:断路器 microservices.io/patterns/re…

外部化配置

每一个业务应用都有许多用于各类基础设施的配置参数(例如,数据库、网络、链接的服务地址、凭据、证书路径)。此外在企业应用程序一般部署在各类运行环境(Local、 Dev、 Prod)中,实现这些的一个方法是经过内部配置。这是一个致命糟糕实践,它会致使严重的安全风险,由于生产凭证很容易遭到破坏。此外,配置参数的任何更改都须要从新构建应用程序,这在在微服务架构中会更加严峻,由于咱们可能拥有数百个服务。

更好的方法是将全部配置外部化,使得构建过程与运行环境分离,生产的配置文件只在运行时或经过环境变量使用,从而最小化了安全风险。

优势

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

缺点

  • 咱们须要选择一个支持外部化配置的框架。

什么时候使用外部化配置

  • 任何重要的生产应用程序都必须使用外部化配置。

什么时候不宜使用外部化配置

  • 在验证概念的开发中。

可用技术示例

几乎全部企业级的现代框架都支持外部化配置。

延伸阅读

微服务模式:外部化配置 microservices.io/patterns/ex…

一次构建,处处运行:外部化你的配置 reflectoring.io/externalize…

消费端驱动的契约测试

在微服务架构中,一般有许多有不一样团队开发的微服务。这些微型服务协同工做来知足业务需求(例如,客户请求),并相互进行同步或异步通讯。消费端微服务的集成测试具备挑战性,一般用 TestDouble 以得到更快、更低成本的测试运行。可是 TestDouble 一般并不能表明真正的微服务提供者,并且若是微服务提供者更改了它的 API 或 消息,那么 TestDouble 将没法确认这些。另外一种选择是进行端到端测试,尽管它在生产以前是强制性的,但倒是脆弱的、缓慢的、昂贵的且不能替代集成测试(Test Pyramid)。

在这方面消费端驱动的契约测试能够帮助咱们。在这里,负责消费端微服务的团队针对特定的服务端微服务,编写一套包含了其请求和预期响应(同步)或消息(异步)的测试套件,这些测试套件称为显式的约定。对于微服务服务端,将其消费端全部约定的测试套件都添加到其自动化测试中。当特定服务端微服务的自动化测试执行时,它将一块儿运行本身的测试和约定的测试并进行验证。经过这种方式,契约测试能够自动的帮助维护微服务通讯的完整性。

优势

  • 若是提供程序意外更改 API 或消息,能够被快速的自动发现。
  • 更少意外、更健壮,特别是包含大量微服务的企业应用程序。
  • 改善团队自主性。

缺点

  • 须要额外的工做来开发和集成微服务服务端的契约测试,由于他们可能使用彻底不一样的测试工具。
  • 若是契约测试与真实服务状况不匹配,将可能致使生产故障。

什么时候使用需求驱动的契约测试

  • 在大型企业业务应用程序中,一般由不一样的团队开发不一样服务。

什么时候不宜使用消费端驱动的契约测试

  • 全部微服务由同一团队负责开发的小型简单的应用程序。
  • 若是服务端微服务是相对稳定的,而且不处在活跃的开发状态。

可用技术示例

Pact, Postman, Spring Cloud Contract

延伸阅读

需求驱动契约:一种服务演进模式 martinfowler.com/articles/co…

微服务模式:服务集成契约测试 microservices.io/patterns/te…

什么是消费端驱动的契约测试? pactflow.io/what-is-con…

总结

在现代大规模企业软件开发中,微服务架构可以帮助开发扩展规模并带来不少长期收益。可是微服务架构并非随处可用的银弹,若是应用在错误的应用程序类型,微服务架构将弊大于利。但愿采用微服务架构的开发团队应该遵循最佳实践,并使用一系列可重用的、久经锤炼的设计模式。

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

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

相关文章
相关标签/搜索