微服务架构中10个常用的设计模式
微服务是一种架构风格,它将一个复杂的应用拆分成多个独立自治的服务,每个服务负责应用程序中的一小部分功能。这些服务通过定义良好的API进行通信,通常是HTTP RESTful API或事件流。微服务架构的主要特点包括单一职责、自治性、可独立部署和扩展、技术异构性以及高可靠性。
微服务架构及其十大关键设计模式:深入解析现代软件架构的核心构建模块。从服务发现到容器化部署,掌握这些设计模式,助力您构建高效、可扩展的微服务系统。
- 1 微服务架构
- 微服务架构的重要特征
- 微服务架构的优点
- 微服务架构的缺点
- 何时使用微服务架构
- 2 微服务架构的设计模式
- 独享数据库(Database per Microservice)
- 事件源(Event Sourcing)
- 命令和查询职责分离(CQRS)
- Saga
- 面向前端的后端 (BFF)
- API 网关
- Strangler
- 断路器
- 外部化配置
- 消费端驱动的契约测试
- 3 总结
自1960年代起,大型软件系统的复杂性便成为业界挑战。软件精英们持续探索简化之道:David Parnas于1972年提出模块化和封装,Edsger W. Dijkstra于1974年倡导关注点分离,直至1988年SOA的崛起。这些创新方法共同为软件架构的简化与优化铺平了道路,持续推动着软件行业的进步。
他们曾借助分而治之技术应对系统复杂性,但自2010年起,这一传统方法已难以应对Web及大型企业应用的挑战。为此,架构师与工程师们创新性地引入了微服务架构,它继承了分而治之的智慧,却以全新方式呈现,成为解决复杂性的现代利器。
软件设计模式是高效解决设计难题的通用方案,避免重复造轮子。现在,让我们快速了解微服务架构,掌握其经实战检验的核心理念。
通过阅读这篇文章,你会学到:
- 微服务架构
- 微服务架构的优势
- 微服务架构的劣势
- 何时使用微服务架构
最重要的微服务架构设计模式,包括其优缺点、用例、上下文、技术栈示例及可用资源。
1 微服务架构
什么是微服务架构?有很多种定义方法。我的定义是这这样的:
微服务架构将大型系统细分为独立部署的子系统,以功能或业务需求为导向。这些子系统通过轻量级、跨语言的同步(如REST、gRPC)或异步(消息)通信,实现高效、灵活的系统构建。
下面是基于微服务架构的商业 Web 应用的组件视图:
来自 Md Kamaruzzaman 的微服务架构
微服务架构的重要特征
- 整个应用程序被拆分成相互独立但包含多个内部模块的子进程
- 微服务应用程序颠覆传统,依据业务范畴或领域垂直分割,与模块化单体应用及SOA形成鲜明对比,实现高效灵活的服务架构。
- 微服务边界是外部的,微服务之间通过网络调用(RPC 或消息)相互通信。
- 微服务是独立的进程,它们可以独立部署。
- 它们以轻量级的方式进行通信,不需要任何智能通信通道。
微服务架构的优点
- 更好的开发规模
- 更快的开发速度
- 支持迭代开发或现代化增量开发
- 支持水平缩放和细粒度缩放
- 小体量,较低了开发人员的认知复杂性
微服务架构的缺点
- 更高数量级的活动组件(服务、数据库、进程、容器、框架)
- 复杂性从代码转移到基础设施
- RPC 调用和网络通信的大量增加
- 整个系统的安全性管理更具有挑战性
- 整个系统的设计变得更加困难
- 引入了分布式系统的复杂性
何时使用微服务架构
- 大规模 Web 应用开发
- 跨团队企业级应用协作开发
- 长期收益优先于短期收益
- 团队拥有能够设计微服务架构的软件架构师或高级工程师
采用Spring Boot、MyBatis Plus、Vue & Element,我们打造了一款功能强大的后台管理系统及用户小程序。系统支持RBAC动态权限、多租户、数据权限,集成工作流、三方登录、支付、短信和商城模块,一站式满足您的业务需求。
2 微服务架构的设计模式
独享数据库(Database per Microservice)
转向微服务架构时,数据库决策至关重要。传统单体架构依赖大型中央数据库,但微服务架构呼吁独立数据服务。尽管维持原数据库短期内看似便利,却易导致系统深度耦合,进而威胁迁移成功。避免数据库层耦合,是实现微服务架构独立开发、团队授权等目标的关键。
推荐为每个微服务配置独立数据存储,实现数据库层的解耦。此处的“数据库”指逻辑数据隔离,允许微服务共享物理数据库但保持数据结构、集合或表的独立。此举确保微服务遵循领域驱动设计原则,实现精准拆分。
Md Kamaruzzaman 的微服务独享数据库
优点
- 数据由服务完全所有
- 服务的开发团队之间耦合度降低
缺点
- 服务间的数据共享变得更有挑战性
- 在应用范围的保证 ACID 事务变得困难许多
- 细心设计如何拆分单体数据库是一项极具挑战的任务
何时使用独享数据库
- 在大型企业应用程序中
- 当团队需要完全把控微服务以实现开发规模扩展和速度提升
何时不宜使用独享数据库
- 在小规模应用中
- 如果是单个团队开发所有微服务
可用技术示例
事件源(Event Sourcing)
在微服务架构中,特别是在独享数据库环境下,微服务间数据交换至关重要。为实现弹性、可伸缩及容错系统,推荐通过异步事件交换进行通信。当面临大数据量的分布式场景时,关系数据库的两阶段锁协议(2PL)因伸缩性受限而不再适用。而NoSQL数据库因其不支持2PL,分布式事务实现困难,需另寻解决之道。
在关键场景中,推荐采用事件源模式。与传统数据库直接存储业务实体当前状态不同,事件源模式侧重于存储所有“状态”更新和重要事件,而非实体本身。这种方式将业务实体的所有变更记录为一系列不可更改的事件。数据以服务的形式,通过重放事件存储中的记录,精确计算所需状态,确保数据的一致性和可追溯性。
Md Kamaruzzaman 的事件源
优点
- 为高可伸缩系统提供原子性操作
- 自动记录实体变更历史,包括时序回溯功能
- 松耦合和事件驱动的微服务
缺点
- 从事件存储中读取实体成为新的挑战,通常需要额外的数据存储(CQRS 模式)。
- 系统整体复杂性增加了,通常需要领域驱动设计。
- 系统需要处理事件重复(幂等)或丢失
- 变更事件结构成为新的挑战。
何时使用事件源
- 使用关系数据库的、高可伸缩的事务型系统
- 使用 NoSQL 数据库的事务型系统
- 弹性高可伸缩微服务架构
- 典型的消息驱动或事件驱动系统(电子商务、预订和预约系统)
何时不宜使用事件源
- 使用 SQL 数据库的低可伸缩性事务型系统
- 在服务可以同步交换数据(例如,通过 API)的简单微服务架构中。
可用技术示例
事件存储首选:EventStoreDB,针对实时事件追踪;Apache Kafka,分布式流处理平台;Confluent Cloud,Kafka即服务;AWS Kinesis,大数据分析;Azure Event Hub,高吞吐事件收集;GCP Pub/Sub,全球消息传递;Azure Cosmos DB、MongoDB、Cassandra,适用于NoSQL存储;Amazon DynamoDB,高性能键值存储。
高效框架集萃:Lagom、Akka、Spring、akkatecture、Axon、Eventuate,构建稳健分布式系统的不二之选。
命令和查询职责分离(CQRS)
采用事件源时,从存储中读取数据变得复杂。需处理所有实体事件以获取数据,且读写操作常面临不同的一致性和吞吐量挑战。
推荐使用CQRS模式,实现数据修改(命令)与读取(查询)的解耦。该模式分为简单与高级两种形式,精准满足系统需求,助力提升数据处理效率。
在其简单形式中,不同实体或 ORM 模型被用于读写操作,如下所示:
Md Kamaruzzaman 的 CQRS (简单)
它有助于强化单一职责原则和分离关注点,从而实现更简洁的设计。
高级CQRS结合事件源模式,通过分离读写操作,实现不同类型的数据存储。写数据存储作为“记录的系统”,是系统核心源头,确保数据的完整性和可追溯性。
Md Kamaruzzaman 的 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,键值存储翘楚。满足各类数据存储需求,助您轻松构建高效应用。
存储技术精选:ElasticSearch助力实时搜索,Solr提供强大全文检索,Cloud Spanner实现全球分布式数据库,Amazon Aurora提供高性能关系数据库,Azure Cosmos DB与Neo4j则分别擅长多模型与图数据库。
专业框架集萃:Lagom、Akka、Spring构筑稳定基石,akkatecture、Axon、Eventuate引领事件驱动新潮流。
Saga
微服务若采用独享数据库,分布式事务管理一致性成挑战。传统两阶段提交协议不适用,因关系数据库不可伸缩,非关系数据库多不支持。解决之道,亟待探索。
但您还是可以在微服务架构中使用 Saga 模式实现分布式事务。Saga 是 1987 年开发的一种古老模式,是关系数据库中关于大事务的一个替代概念。但这种模式的一种现代变种对分布式事务也非常有效。Saga 模式是一个本地事务序列,其每个事务在一个单独的微服务内更新数据存储并发布一个事件或消息。Saga 中的首个事务是由外部请求(事件或动作)初始化的,一旦本地事务完成(数据已保存在数据存储且消息或事件已发布),那么发布的消息或事件则会触发 Saga 中的下一个本地事务。
Md Kamaruzzaman 的 Saga
如果本地事务失败,Saga 将执行一系列补偿事务来回滚前面本地事务的更改。
Saga 事务协调管理主要有两种形式:
- 事件编排Choreography:微服务间分散协调,生产并监听事件/消息,自主决策执行动作,实现高效协同。
优点
- 为高可伸缩或松耦合的、事件驱动的微服务架构提供一致性事务。
- 为使用了不支持 2PC 的非关系数据库的微服务架构提供一致性事务。
缺点
- 需要处理瞬时故障,并且提供等幂性。
- 难以调试,而且复杂性随着微服务数量增加而增加。
何时使用 Saga
- 在使用了事件源的高可伸缩、松耦合的微服务中。
- 在使用了分布式非关系数据库的系统中。
何时不宜使用 Saga
- 使用关系数据库的低可伸缩性事务型系统。
- 在服务间存在循环依赖的系统中。
可用技术示例
Axon, Eventuate, Narayana
面向前端的后端 (BFF)
在现代商业应用及微服务架构中,前后端应用分离并通过API或GraphQL连接。若涉及移动App,共享后端微服务会面临挑战。由于移动与Web客户端在屏幕尺寸、性能、能耗和网络带宽上存在差异,其API需求各异。因此,确保两者API的差异化设计与实现至关重要。
前端定制后端模式专为特殊UI打造,不仅减少UI与下游微服务通信,还作为微服务封装提升效率。尤其在高安全需求场景下,BFF为DMZ网络中的微服务提供卓越安全保障,是前端与后端高效协作的优选方案。
Md Kamaruzzaman 的面向前端的后端
优点
- 分离 BFF 之间的关注点,使得我们可以为具体的 UI 优化他们。
- 提供更高的安全性
- 减少 UI 和下游微服务之间频繁的通信
缺点
- BFF 之间代码重复
- 大量的 BFF 用于其他用户界面(例如,智能电视,Web,移动端,PC 桌面版)
何时使用 BFF
- 如果应用程序有多个含不同 API 需求的 UI
- 出于安全需要,UI 和下游微服务之间需要额外的层。
- 如果在 UI 开发中使用微前端。
何时不宜使用 BFF
- 如果应用程序虽有多个 UI,但使用的 API 相同。
- 如果核心微服务不是部署在 DMZ 网络中。
可用技术示例
全面兼容主流后端框架,包括Node.js、Spring、Django、Laravel、Flask、Play等,为您的项目提供强大支撑。
API 网关
微服务架构中,UI常需连接众多微服务,尤其细粒度(FaaS)服务,客户端面临连接繁杂挑战。服务及其API持续演进,大型企业更需应对SSL终止、身份验证、授权等横切关注点。如何高效管理这些连接与关注点,成为微服务架构优化的关键。
解决客户端与后端微服务间通信难题,推荐采用API网关。它作为facade,不仅作为反向代理将请求路由至对应服务,还能将请求分发至多个微服务并聚合响应,高效满足需求。此外,网关还支持关键横切关注点,为系统提供全面保障。
Md Kamaruzzaman 的 API 网关
优点
- 在前端和后端服务之间提供松耦合
- 减少客户端和微服务之间的调用次数
- 通过 SSL 终端、身份验证和授权实现高安全性
- 集中管理的横切关注点,例如,日志记录和监视、节流、负载平衡。
缺点
- 可能导致微服务架构中的单点故障
- 额外的网络调用带来的延迟增加
- 如果不进行扩展,它们很容易成为整个企业应用的瓶颈。
- 额外的维护和开发费用
何时使用 API 网关
- 在复杂的微服务架构中,它几乎是必须的。
- 在大型企业中,API 网关是中心化安全性和横切关注点的必要工具。
何时不宜使用 API 网关
- 在安全和集中管理不是最优先要素的私人项目或小公司中。
- 如果微服务的数量相当少。
可用技术示例
API管理平台精选:Amazon API Gateway,Azure API Management,Apigee,Kong,WSO2 API Manager,助力高效API管理。
Strangler
在项目中引入微服务架构,需将单体应用迁移至微服务。此举虽具挑战性,可能影响应用可用性,但实现后,将显著提升系统灵活性和可扩展性,助力业务飞速发展。
一个解决方案是使用 Strangler 模式。Strangler 模式意味着通过使用新的微服务逐步替换特定功能,将单体应用程序增量地迁移到微服务架构。此外,新功能只在微服务中添加,而不再添加到遗留的单体应用中。然后配置一个 Facade (API 网关)来路由遗留单体应用和微服务间的请求。当某个功能从单体应用迁移到微服务,Facade 就会拦截客户端请求并路由到新的微服务。一旦迁移了所有的功能,遗留单体应用程序就会被“扼杀(Strangler)”,即退役。
Md Kamaruzzaman 的 Strangler
优点
- 安全的迁移单体应用程序到微服务
- 可以并行地迁移已有功能和开发新功能
- 迁移过程可以更好把控节奏
缺点
- 在现有的单体应用服务和新的微服务之间共享数据存储变得具有挑战性
- 添加 Facade (API 网关)将增加系统延迟
- 端到端测试变得困难
何时使用 Strangler
- 将大型后端单体应用程序的增量迁移到微服务
何时不宜使用 Strangler
- 如果后端单体应用很小,那么全量替换会更好。
- 如果无法拦截客户端对遗留的单体应用程序的请求。
可用技术示例
API 网关后端应用框架。
断路器
在微服务架构中,同步调用满足业务需求,但瞬时故障常导致服务调用失败。轻量级问题可通过重试解决,但面对微服务完全失效的严重问题,长时间重试既无效又耗资源。此时,快速失败策略更为明智,避免线程阻塞、CPU资源浪费,同时预防整个应用系统的级联故障,确保系统稳健运行。
断路器模式在此情境下尤为适用。通过代理,一个微服务请求另一微服务,其机制如电气断路器般智能,根据故障统计判断是否继续请求或直接返回异常,确保系统稳定性。
Md Kamaruzzaman 的断路器
断路器可以有以下三种状态:
- 打开:来自微服务的请求会快速失败并返回异常。在超时后,断路器进入半开启状态。
优点
- 提高微服务架构的容错性和弹性
- 阻止引发其他微服务的级联故障
缺点
- 需要复杂的异常处理
- 日志和监控
- 应该支持人工复位
何时使用断路器
- 在微服务间使用同步通信的紧耦合的微服务架构中
- 如果微服务依赖多个其他微服务
何时不宜使用断路器
- 松耦合、事件驱动的微服务架构
- 如果微服务不依赖于其他微服务
可用技术示例
API网关、服务网格与断路器库(Hystrix、Resilience4J、Polly)助力构建稳健、高效的微服务架构。
外部化配置
业务应用配置参数繁多,涵盖数据库、网络及服务地址等。企业应用常部署于多环境(Local、Dev、Prod),依赖内部配置易引发安全风险,生产凭证易受侵害。此做法不仅风险高,且配置变更需重建应用,微服务架构下问题尤为严峻,因服务数量可能高达数百。优化配置管理,确保业务安全稳定至关重要。
最佳实践是将配置全部外部化,实现构建与运行环境分离。仅运行时或经环境变量使用配置文件,大幅减少安全风险。
优点
- 生产配置不属于代码库,因而最小化了安全漏洞。
- 修改配置参数不需要重新构建应用程序。
缺点
- 我们需要选择一个支持外部化配置的框架。
何时使用外部化配置
- 任何重要的生产应用程序都必须使用外部化配置。
何时不宜使用外部化配置
- 在验证概念的开发中。
可用技术示例
几乎所有企业级的现代框架都支持外部化配置。
消费端驱动的契约测试
在微服务架构中,通常有许多有不同团队开发的微服务。这些微型服务协同工作来满足业务需求(例如,客户请求),并相互进行同步或异步通信。消费端微服务的集成测试具有挑战性,通常用 TestDouble 以获得更快、更低成本的测试运行。但是 TestDouble 通常并不能代表真正的微服务提供者,而且如果微服务提供者更改了它的 API 或 消息,那么 TestDouble 将无法确认这些。另一种选择是进行端到端测试,尽管它在生产之前是强制性的,但却是脆弱的、缓慢的、昂贵的且不能替代集成测试(Test Pyramid)。
消费端驱动的契约测试是保障微服务通信的关键。消费端团队针对服务端微服务,编写包含请求、预期响应(同步)或消息(异步)的显式约定测试套件。服务端则集成这些测试套件于自动化测试流程中。每当服务端测试执行时,它都会同时验证自有逻辑与消费端约定,确保通信的完整性得以自动维护,提升微服务交互的可靠性。
优点
- 如果提供程序意外更改 API 或消息,可以被快速的自动发现。
- 更少意外、更健壮,特别是包含大量微服务的企业应用程序。
- 改善团队自主性。
缺点
- 如果契约测试与真实服务情况不匹配,将可能导致生产故障。
何时使用需求驱动的契约测试
- 在大型企业业务应用程序中,通常由不同的团队开发不同服务。
何时不宜使用消费端驱动的契约测试
- 所有微服务由同一团队负责开发的小型简单的应用程序。
- 如果服务端微服务是相对稳定的,并且不处在活跃的开发状态。
可用技术示例
Pact, Postman, Spring Cloud Contract
采用Spring Cloud Alibaba技术栈,融合Gateway、Nacos、RocketMQ及Vue & Element,我们精心打造了一款后台管理系统与用户小程序。该系统支持RBAC动态权限、多租户、数据权限、工作流等核心功能,并集成三方登录、支付、短信、商城等扩展服务,全方位满足业务需求。
3 总结
现代大规模企业软件开发中,微服务架构是扩展规模的利器,长期效益显著。然而,它并非万能解药,错用将适得其反。为确保成功,开发团队需遵循最佳实践,运用经过验证的可重用设计模式,方能充分发挥微服务架构的优势。
微服务架构中至关重要的设计模式是独享数据库。实现这种设计模式具有挑战性,需要其他几种密切相关的设计模式(事件驱动、 CQRS、 Saga)来支持。在具有多个客户端(Web、 Mobile、 Desktop、 Smart Devices)的典型业务应用程序中,客户端和微服务之间的通信量可能是很大的,并且需要统一的安全控制,在这种情况面向前端的后端和 API 网关的设计非常有用。此外,断路器模式可以大大地帮助应对这类应用程序的错误处理场景。迁移遗留的单体应用到微服务是极具挑战性的,而 Strangler 模式可以帮助做到这点。消费端驱动的契约测试是微服务集成测试的基础模式。另外外部化配置是任何现代化应用程序开发中的一种必备模式。
-对此,您有什么看法见解?-
-欢迎在评论区留言探讨和分享。-