微服务架构中10个常用的设计模式,建议收藏!

微服务架构中10个常用的设计模式,建议收藏!
微服务架构中10个常用的设计模式,建议收藏!

简介

自 2010 年代以来,随着大型 Web 级应用和现代企业系统的蓬勃发展,传统的模块化和组件化技术已难以应对系统规模、团队扩展和高可用性的多重挑战。在这种背景下,微服务架构 (Microservices Architecture) 成为应对大型系统复杂性的行业标准。微服务架构将大型单体系统垂直切分成多个独立部署、独立演进的子进程,子进程之间通过轻量级的网络协议进行通信。

然而,微服务并非银弹。它在降低了单个服务认知复杂度的同时,也引入了分布式系统的天然复杂性(如数据一致性、网络延迟、安全边界、监控追踪等)。为了在这个复杂的分布式环境中构建出健壮、可扩展且易于维护的系统,我们需要遵循一系列行之有效的软件设计模式。本文将深入探讨微服务架构中最常用的 10 个核心设计模式,分析其优缺点、适用场景和技术选型,帮助你在实际项目中进行优雅的架构设计。


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

在从单体系统迁移到微服务架构时,最核心的决策之一就是数据层的切分。许多团队倾向于保持原有的单一中心化数据库不变,让所有微服务共享同一个数据库。这种做法虽然在短期内能快速上线,但在长期来看是极具危害的反模式。共享数据库会导致微服务在数据层发生隐式耦合,导致某个团队修改表结构时,其他团队的服务面临崩溃风险,彻底抹杀了微服务独立开发、独立部署的优势。

独享数据库模式要求每个微服务拥有并控制其私有数据存储。其他服务无法直接访问该数据库,只能通过该服务暴露的 API 接口进行数据存取。这确保了服务之间在数据层完全解耦,并且有助于团队根据业务边界(领域驱动设计,DDD)进行架构边界的规划。

Mermaid Diagram

优点: - 彻底解耦:服务的开发团队可以完全独立地设计和修改表结构,而无需与其他团队协调。 - 技术多样性:不同微服务可以根据其业务数据的特点,选择最合适的数据存储技术(例如,商品检索使用 Elasticsearch,事务订单使用 PostgreSQL,用户缓存使用 Redis)。

缺点: - 数据共享困难:跨服务的数据关联查询(Join)变得非常复杂,无法直接通过一条 SQL 语句解决。 - 分布式事务挑战:跨数据库的数据写入无法依靠数据库底层的 ACID 特性,保证全局一致性变得极其困难。

适用场景: - 大型企业级微服务应用,需要团队之间高度自治、快速迭代。 - 各服务数据吞吐量大、访问模式差异显著的系统。

不适用场景: - 小型单团队维护的微服务应用。 - 业务逻辑简单,且需要频繁跨表强一致性关联查询的系统。


2. 事件源 (Event Sourcing)

在微服务独享数据库的背景下,微服务之间需要频繁同步状态或传递变化。如果直接使用同步的网络调用来同步数据,很容易因为某个服务暂时不可用而导致整个业务链条瘫痪。同时,对于高并发、可伸缩的交易系统,每次操作都同步修改数据库状态并向外发送消息,其非原子性操作很容易导致消息丢失或状态不一致。

事件源模式提供了一种全新的思路:系统不存储实体的当前状态,而是将所有改变状态的动作作为一系列不可变事件 (Events) 存储在持久化日志中。这些事件按照发生的时间顺序追加到事件存储(Event Store)中。当需要获取实体的当前状态时,系统可以通过从头(或从最近的快照)“重放”该实体的所有历史事件,计算出其最新状态。这为分布式系统提供了绝对可靠的数据审计日志和原子性变更机制。

Mermaid Diagram

优点: - 完美审计与回溯:由于记录了每一次的状态变更事件,系统具备天然的审计日志,并且可以轻松实现时光回溯,定位任意历史时刻的系统状态。 - 高并发原子写入:事件存储采用仅追加(Append-only)的写入方式,避免了复杂的数据库锁竞争,极大地提升了写操作的吞吐量与响应速度。

缺点: - 读取延迟与计算成本:由于需要重放大量事件来获取实体状态,直接读取的性能较低,通常需要结合 CQRS(命令查询职责分离)进行读写分离。 - 学习曲线陡峭:需要开发人员转变思维模式,引入领域驱动设计(DDD)中的聚合根与领域事件概念,且事件结构的变更(Schema Evolution)维护成本较高。

常用技术栈: - 事件存储:EventStoreDB, Apache Kafka, Confluent Cloud, Amazon DynamoDB, Azure Cosmos DB。 - 应用框架:Lagom, Akka, Axon, Eventuate。


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

在采用了事件源模式或微服务独享数据库后,系统的数据修改(写)和数据读取(读)往往会面临不同的性能与结构要求。如果使用同一套数据模型同时处理高并发的写入和复杂的统计查询,会导致系统设计臃肿、数据库索引配置困难、甚至引发严重的锁竞争。

CQRS(Command Query Responsibility Segregation)模式主张将系统的写操作(Command,用于修改数据)与读操作(Query,用于读取数据)从逻辑结构上彻底分离。在高级形式中,写操作和读操作甚至使用不同的数据库。写数据库作为“记录系统”,保存高度规范化的事务数据;读数据库则保存针对查询场景特殊优化过的、非规范化的数据(物化视图)。写库中的变更通过消息总线以事件的形式异步同步到读库中。

微服务架构中10个常用的设计模式,建议收藏!

Mermaid Diagram

优点: - 独立扩展:读操作和写操作的数据模型与存储技术可分别独立扩展。例如,写操作使用强一致性 PostgreSQL,读操作使用高度优化的 Elasticsearch。 - 极致查询性能:读库中直接存储的是根据 UI 展示需求定制的扁平数据模型,无需进行复杂的 Join 计算,大幅降低了查询响应时间。

缺点: - 最终一致性:由于数据在读写库之间是异步复制的,读取端会存在短暂的感知延迟,需要业务系统能够容忍最终一致性(Eventual Consistency)。 - 架构复杂度:维护两个独立的数据库和数据映射管道,显著增加了系统的运维与开发成本。

常用技术栈: - 写库:EventStoreDB, PostgreSQL, MongoDB, Cassandra。 - 读库/搜索引擎:Elasticsearch, Apache Solr, Redis, Amazon Aurora。


4. Saga 分布式事务模式

在微服务架构中,一个完整的业务流程常常需要跨越多个独立的微服务和数据库。例如,在电商系统“下单”流程中,需要订单服务创建订单、库存服务扣减库存、支付服务扣款。传统的单体系统可以使用数据库本地事务(ACID)轻松搞定,但在每个服务独享数据库的分布式场景下,我们无法使用传统的两阶段提交协议(2PC),因为 2PC 会导致严重的资源锁定和长网络延迟,极大限制了系统的吞吐量。

Saga 模式通过将一个全局分布式事务拆分为一系列本地事务链来解决这个问题。每个本地事务仅在其所属的微服务内更新其私有数据库。当某个本地事务成功提交后,它会发布一个事件或消息,以此触发下一个微服务的本地事务。如果链条中的某个事务执行失败(例如库存不足),Saga 将通过反向执行补偿事务 (Compensating Transactions),依次撤销此前所有成功步骤所作的修改,从而实现数据的最终一致性。

Mermaid Diagram

Saga分布式事务与数据库隔离

Saga 的协调管理主要有两种实现形式:

1. 协同编排 (Choreography):分散式协调。每个微服务直接发布和监听其他服务的事件,并根据事件内容自行决定是否执行下一步骤或补偿步骤。

2. 命令编排 (Orchestration):集中式协调。由一个专门的 Saga 协调器 (Orchestrator) 负责集中指派和监控各个微服务本地事务的执行顺序。

优点: - 高并发与可伸缩性:服务之间通过异步消息传递,没有长时间的全局资源锁定,能支持极高的交易吞吐量。 - 兼容非关系型数据库:即使下游服务使用的是不支持 2PC 的 NoSQL 数据库(如 MongoDB),依然可以实现分布式事务控制。

缺点: - 设计复杂性:必须为每一个正向业务步骤设计对应的反向补偿逻辑,且需要处理因为网络延迟或重试带来的幂等性(Idempotency)和瞬时故障问题。 - 隔离性缺失:在 Saga 事务执行的中途,其他并发请求可能会看到中间状态,这需要从业务层面进行适当设计。


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

随着终端设备的多样化,现代商业应用通常包含 Web 端、移动 App(iOS/Android)、PC 客户端和智能设备等多个前端。如果所有前端设备都直接调用相同的后端微服务,会面临显著的接口适配痛点。移动端因为屏幕尺寸小、网络带宽受限,需要极度精简、高度聚合的接口;而 Web 端屏幕大,可以展示更多字段,并希望接口具有更强的灵活性。

BFF(Backend For Frontend)模式提倡为每一种特定的前端客户端,设计一个专属的后端网关/适配服务。BFF 部署在 DMZ(隔离区)网络中,充当前端和后端核心微服务之间的中介层。它负责将多个下游微服务的细粒度 API 接口进行编排、过滤和聚合,最终为特定前端设备提供高度定制、极简的契约接口。

Mermaid Diagram

BFF与API网关接入层架构

优点: - 关注点分离:前端开发团队可以高度控制对应的 BFF 服务,自主定制最符合 UI 展示的接口格式,实现前后台的敏捷迭代。 - 减少网络交互:BFF 可以将前端的多次网络往返(Round-trip)请求在后端内网聚合为单次请求,极大改善了移动端高延迟环境下的用户体验。 - 增强安全防护:BFF 隐藏了下游微服务的具体网络拓扑,提供了统一的安全策略(如 Token 校验、限流防刷)。

缺点: - 代码重复:不同的 BFF 之间可能会存在部分相似的数据格式转化和中转逻辑,需要进行合理的模块化抽取。 - 额外层引入:增加了一层网络跳数,若设计不当可能会引入额外的服务延迟和单点故障风险。


6. API 网关模式 (API Gateway)

在大规模微服务集群中,客户端如果直接面对数十甚至上百个细粒度的微服务,会面临繁琐的网络配置和安全漏洞。每个服务都需要独立处理域名路由、SSL 证书证书校验、用户鉴权、流量整形等横切关注点(Cross-cutting Concerns),这不仅造成严重的配置冗余,也增加了系统整体的安全受攻击面。

API 网关模式通过在客户端与核心微服务之间引入一个统一的入口Facade来解决这个问题。所有的客户端请求必须首先到达 API 网关。网关负责执行以下核心职责: - 请求路由:将外部请求的虚拟 URL 路径映射并转发到后端的具体微服务。 - 横切关注点集中化:在网关处统一处理身份认证、授权验证、SSL 证书终止、全局限流、熔断降级。 - 负载均衡:与服务注册中心(如 Consul/Nacos)集成,将流量均匀分发到可用实例。

Mermaid Diagram

优点: - 隔离与松耦合:客户端无需知晓微服务的网络位置及 IP,后端微服务可以自由迁移、合并或重构,而无需修改客户端逻辑。 - 全局安全管控:实现了一站式的身份认证与防火墙控制,大幅减轻了底层微服务的开发负担。

缺点: - 单点故障风险:API 网关是系统的流量咽喉,一旦网关崩溃,整个微服务群将彻底瘫痪,必须进行高可用性部署。 - 性能瓶颈:如果未对网关进行异步非阻塞改造或限流配置,大并发下网关容易成为系统吞吐量的最大瓶颈。

常用技术栈: - Amazon API Gateway, Kong, Apigee, Spring Cloud Gateway, Nginx, APISIX。


7. 绞杀者模式 (Strangler Fig Pattern)

当企业决定将已有的大型单体应用升级为微服务架构时,直接抛弃原有单体并进行“推倒重来”往往是极具高风险的灾难选择。这可能导致线上服务长期中断、新旧系统业务功能对齐困难、甚至引发严重的资损事件。

绞杀者模式(Strangler Fig)借鉴了绞杀藤围绕大树寄生并最终取代宿主的自然现象,主张通过逐步剥离和替换单体应用的特定功能模块,实现新微服务的增量演进和无缝替换。在演进过程中,新功能只在新的微服务中编写,而遗留单体则不再新增任何代码。通过配置一个 API 网关(或 Facade 反向代理),将已经被剥离出的业务流量平滑路由到新微服务,而未被替换的流量继续路由到老单体。最终,当所有功能替换完毕,单体应用便会被完全“绞杀”并退役。

Mermaid Diagram

优点: - 极佳的安全性:由于采用了逐步迭代、按模块上线的增量演进方式,系统的演进节奏完全可控,一旦出现线上故障,能立刻将流量切回旧单体,具备天然的回滚防线。 - 业务开发不停摆:新功能的开发与旧功能的迁移可以并行不悖地展开,不需要长达数月的“业务冻结期”。

缺点: - 数据共享的阵痛:在新微服务和老单体数据库之间,可能需要长期维护双向同步管道,容易面临短暂的数据不一致挑战。 - 链路链路复杂化:演进中期系统会呈现新老交织的状态,给端到端集成测试和线上监控带来了极大的难度。


8. 断路器模式 (Circuit Breaker)

在微服务拓扑中,微服务之间经常会发生错综复杂的链式网络调用。如果某个处于底层的关键微服务(例如数据库访问模块)由于网络抖动、线程池打满或硬件故障突然崩溃,调用该底层的上游服务将会面临超时的等待。随着用户并发请求的不断涌入,上游服务的请求线程会被大量阻塞,最终导致整个系统的线程池被打满、CPU 使用率飙升,引发不可挽回的级联雪崩(Cascading Failure)。

断路器模式借鉴了电气工程中的自动保险丝机制,在同步的网络调用代理处,统计最近发生的网络错误频率。如果故障率超过预设的阈值,断路器便会自动“跳闸”,防止继续将请求发送到已经瘫痪的下游,从而实现快速失败

Mermaid Diagram

断路器包含三种标准状态:

1. 关闭 (Closed):正常状态。所有流量通过。如果调用失败,开始累计故障计数。

2. 打开 (Open):熔断跳闸状态。所有外部请求快速失败,不再向下游发送任何真实网络调用,而是直接返回降级数据或默认异常。

3. 半开 (Half-Open):冷却期过后的尝试状态。断路器允许极少量的测试流量通过,如果这些测试请求能够全部成功,则断路器自动闭合,系统恢复正常;如果再次出现任何失败,则瞬间切换回打开状态,重新开启冷却计时。

优点: - 阻止雪崩:及时切断故障源头,防止局部错误演变成全局瘫痪。 - 自我修复空间:给下游瘫痪的服务腾出宝贵的 CPU 与带宽资源,使其能够实现自我修复。

缺点: - 降级逻辑设计成本:开发团队需要为每一个断路器接口,设计合理的“fallback”数据或友好的降级返回逻辑,这通常与具体业务场景强相关。

常用技术栈: - Resilience4j, Hystrix, Polly, Sentinel, Istio / Linkerd (服务网格下沉控制)。


9. 外部化配置 (Externalized Configuration)

任何业务系统在运行时都离不开各种配置参数(如数据库连接字符串、外部 API 网关 URL、各类加解密证书和签名密钥等)。在传统的开发中,团队通常会将这些配置写在项目的 .properties.yml 配置文件中。但在微服务架构下,我们需要面对成百上千个微服务实例,并且需要同时维护开发、测试、预发、生产等多个复杂运行环境。

如果继续将配置硬编码在代码库中,会带来致命的后果: - 安全风险:生产数据库的真实账号与密钥极易由于代码库泄露而被窃取。 - 构建效率低下:每次仅仅修改一个超时时间,都需要对代码进行重新拉取、打包构建、发布上线的完整编译流水线。

外部化配置模式主张将所有运行时配置与代码彻底分离,构建出完全不带任何环境特征的通用“镜像包”,配置参数在容器启动时通过环境变量、命令行参数或集中配置中心进行注入

Mermaid Diagram

优点: - 一处构建,多处运行:构建物与运行环境完全解耦,提升了 CI/CD 的标准化程度。 - 运行时动态更新:许多现代配置中心支持“热加载”,修改配置后无需重启进程即可在线生效。 - 高度安全:生产凭据只保存在受权限控制的运维配置中心,代码库对敏感数据不可见。

缺点: - 配置一致性维护:需要引入专门的配置同步与冲突检测机制,以防止因为配置变更出错而造成线上大范围不可用。

常用技术栈: - Apollo (携程), Spring Cloud Config, Nacos, HashiCorp Vault (偏向密钥安全), Kubernetes ConfigMap / Secret。


10. 消费端驱动的契约测试 (Consumer-Driven Contract Testing)

在庞大的微服务集群中,一个微服务的修改(接口变更)往往会产生连锁反应。例如,用户服务修改了一个字段类型,依赖该服务的订单服务在执行生产部署时可能会突然发生 JSON 解析异常崩溃。在单体应用中,这类接口错误可以通过静态编译校验轻松避免。但在独立的微服务微生态下,传统的端到端测试(E2E)由于耗时极长、测试环境极其脆弱且难以维护,无法作为 CI 门禁的高效检测手段。

消费端驱动的契约测试模式(Consumer-Driven Contract Testing)通过将接口规范显式化并作为可测试实体来解决这个问题。在该模式中:

1. 由接口的消费者 (Consumer) 团队编写契约定义文件,详细说明其对提供者 (Provider) 接口的请求格式以及预期的 JSON 结构响应。

2. 契约文件会作为“最终约定”共享给提供者团队。

3. 提供者团队将消费者的契约测试套件直接集成到自己的本地 CI/CD 流水线中。每次提供者提交代码进行编译时,CI 会自动读取这套契约规范并模拟消费者发送请求,验证其响应是否依然合规。通过这种契约前置验证,可以在合并代码的源头完全杜绝破坏性升级。

Mermaid Diagram

优点: - 自动化防爆升级:在开发和合并分支的最早阶段,便能以极快的速度检测出接口不兼容,彻底免除了端到端集成测试的缓慢成本。 - 提升团队自治性:提供者团队只要保证 CI 契约测试全部通过,便可以大胆重构服务接口与内部代码,无需提心吊胆去通知每一个关联业务方。

缺点: - 前期维护门槛:需要跨越团队进行协同开发,两端需要接入统一的契约测试框架(如 Pact),学习与编写成本较高。 - 契约脱节:必须确保测试契约中的测试用例与生产环境的真实交互百分之百保持同步,否则形同虚设。

常用技术栈: - Pact (业内最成熟事实标准), Spring Cloud Contract, Postman API Network。


总结

微服务架构不是通往高性能的万能灵药,而是解决大型团队在大型系统开发中协同和扩展难题的系统论。在构建微服务系统时,独享数据库是确保服务自治的核心基石。在此基础上,系统往往需要依靠事件源CQRS 以及 Saga 模式来打通数据一致性和复杂读取链路。针对不同终端的前端交互,BFFAPI 网关能够提供极致的安全护航与接口聚合。而在日常的容错与升级演进中,断路器外部化配置以及消费端契约测试则是必不可少的安全防护网。

希望本文介绍的 10 种设计模式能成为你微服务架构设计工具箱中的利器,帮助你根据项目业务特点,进行最优的架构权衡。欢迎大家在下方留言探讨,各抒己见!


[!WARNING] 微服务拆分门禁警告:绝对不要在团队规模小于 15 人、或者系统访问吞吐极小时强行应用微服务独享数据库与分布式事务,这会导致维护成本大幅吞没业务开发时间!


公众号二维码

长按二维码关注 “边学边练”

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有

相关阅读