ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

微服务架构的6种核心模式:拆分、注册、网关、配置、容错与一致性

2026/10/3 2:58:33 拓冰建站 浏览量
微服务架构的6种核心模式:拆分、注册、网关、配置、容错与一致性 做后端开发的几乎都躲不开微服务这三个字。前两天有人问我微服务架构的6种模式到底是指哪六种说实话“6种模式”并不是某个官方标准拆成 7 种、8 种也可以但它反映的确实是微服务架构落地时最常遇到的六类问题边界怎么划、服务怎么找到彼此、入口怎么统一、配置怎么管、故障怎么扛、数据怎么保持一致。这篇文章按这个思路把六种模式逐一拆开讲清楚每种模式解决了什么问题、有哪些实现方式、落地时有哪些坑适合正准备拆微服务或者已经在微服务里挣扎的团队参考。读过一遍你至少能明白那些看起来很硬的架构方案背后其实都是很朴素的取舍逻辑。1. 服务拆分模式一切从“边界”开始1.1 按业务能力拆别按技术层拆微服务架构的第一步不是选框架而是回答“拆成几个服务、每个服务管什么”。很多人一上手就把原来的单体项目按 Controller、Service、Mapper 拆成三个服务这种做法看着省事实际是灾难。一次用户下单操作要同时调用户服务、订单服务、商品服务每个服务之间频繁做远程调用事务没人能管排查问题要在三个服务的日志之间来回跳。这就是典型的“分布式单体”比单体还难维护。正确的拆分思路是按业务能力拆也就是 DDD 里常说的限界上下文。你要先想清楚这个系统里有哪几个核心业务能力比如电商系统可以拆成商品、库存、订单、支付、用户每个能力对应一个独立的服务。每个服务内部可以有自己的分层但服务与服务之间只能通过 API 协作不能直接访问对方的数据库也不应该共享同一个表。判断标准很简单这两个模块是不是围绕着同一个业务目标转如果商品模块改了一个字段订单模块完全不受影响那它们就有拆开的基础如果每次改动都要两边同步改那说明它们本就不该分开。1.2 怎么判断一个服务该不该拆实际决策时我一般看三个维度第一是变更频率两个功能如果总是同时发布、同时修改说明耦合很高拆了以后每次改动都要跨服务发布反而更累第二是团队边界康威定律在微服务里体现得非常明显一个服务最好由一个小而完整的团队长期维护如果团队只有 3 个人却要维护 15 个服务那大概率每个服务都没人真正负责第三是数据边界两个模块能不能拥有各自独立的数据库这是最现实的约束条件很多时候代码能拆数据拆不动最后只能做成假微服务。还有一个很容易踩的坑是拆得太细。电商本来拆成订单、支付、库存就够了有人硬要把优惠券、购物车、结算、物流信息也各拆一个服务结果一个下单页面要组合 5 个服务的接口再加上网络抖动页面响应直接掉了 200 毫秒。我在实际项目里的习惯是拆分初期宁粗勿细先划清业务模型的边界用模块化单体过渡等模块之间确实出现了独立部署、独立伸缩的需求再去动数据库和代码。微服务拆分不是目的团队效率和业务响应速度才是目的。2. 服务注册与发现模式让服务在动态环境里找到彼此2.1 客户端发现和服务端发现单体架构里服务地址可以写死在配置里微服务就不行了因为实例会动态伸缩IP 会随时变化。这时需要一个“通讯录”来维护所有服务实例的地址这就是注册中心。注册发现模式主要分两种实现方式客户端发现和服务端发现。客户端发现问题是服务消费方启动时从注册中心拉取服务提供方的实例列表缓存在本地再自己选择合适的实例发起调用。Spring Cloud 里的 Eureka、Nacos 配合 OpenFeign Spring Cloud LoadBalancer 就是典型的客户端发现模式客户端既负责找服务又负责负载均衡。服务端发现问题是客户端只访问一个固定的代理入口比如 Kubernetes 里的 Service由代理去注册中心查询可用实例并转发请求。两者没有绝对的优劣客户端发现少了代理转发链路更短、延迟更低服务端发现统一了入口客户端更简单也更适合容器编排环境。2.2 注册中心的选型与健康检查选注册中心不能只看名气要看你的运维场景。Eureka 是很经典的老牌方案功能简单自带保护机制但现在已经很少被新项目选择了Consul 强在健康检查和多数据中心运维能力很强Nacos 在国内语境下用得多不光能做注册中心还能当配置中心和 Spring Cloud Alibaba 集成也顺。我的建议是如果团队没有特殊历史包袱优先选 Nacos 或 Consul注册和配置可以共用一套系统少维护一个中间件。注册中心真正让人头疼的不是选型是健康检查的配置。Nacos 里临时实例依赖心跳续约如果心跳超时时间设置太短网络一抖动健康的服务会被误判为不健康并摘除流量瞬间打到剩余实例上造成连锁波动设置太长服务真的挂了上下游要等很久才能感知。我一般会把心跳超时和自动摘除时间调到三到五倍的心跳周期给网络抖动留缓冲。另外要注意客户端本地缓存的问题注册中心摘除实例只是第一步调用方本地可能还缓存着旧地址这种场景下要配合合理的缓存过期和快速失败逻辑而不是无脑重试。2.3 服务发现之后的负载均衡服务发现解决的是“去哪找服务”找到以后还要回答“选哪个实例”。微服务里的负载均衡通常客户端就做了调用方拿到实例列表后用轮询、随机或者加权策略选一个。这个模式看起来简单但我在实践中吃过亏某次 A 服务调用 B 服务B 服务有两台实例一台配置 2G 内存另一台配置 4G默认轮询导致小内存实例频繁被打挂。后来改成加权负载均衡把权重调成和小内存成反比才算消停。还有一个更隐蔽的坑是重试叠加。客户端负载均衡在有多个实例时一个请求超时后会自动重试下一个实例这本来是高可用能力但如果每个环节都重试三次一个请求失败后可能变成十几个实际调用下游很容易被瞬间放大流量打垮。所以负载均衡的重试必须设上限并且配合熔断降级一起用这个问题在后面容错模式里会重点展开。3. API 网关模式微服务的统一入口3.1 没有网关会发生什么如果是早期的单体应用所有请求都打到同一个服务上并不需要网关。微服务拆开以后每个服务都暴露自己的地址客户端要想完成一个业务操作得知道好几个服务的地址还要自己拼参数、处理鉴权、跨域这显然不现实。API 网关模式的本质是把所有进来的请求统一收口到一个入口由网关负责路由转发和横切逻辑处理类似于写字楼的接待前台访客不用知道每个人坐在哪张桌子只说一句“我找订单部门的谁”前台就帮你带路了。网关能做的远不止转发。统一鉴权、接口限流、请求日志、灰度发布、协议转换这些横切关注点都可以收敛到网关层避免每个服务重复实现。尤其在安全方面网关可以屏蔽内部服务细节对外只暴露必要的接口内部服务之间走内网流量暴露面小很多。这也是为什么很多公司接入微服务后会把网关作为整个系统的流量入口和唯一公网边界。3.2 网关的功能边界聚合器模式与 BFF网关模式里常说的聚合器Aggregator就是网关收到一个客户端请求后并行调用多个下游服务把结果组装成一个响应返回。比如打开一个订单详情页需要订单服务、用户服务、商品服务各出一部分数据客户端如果分别调用三个接口要处理三次网络请求和三次失败场景网关聚合后客户端只需要调用一次。这个模式能显著简化客户端但也有代价网关开始承担业务逻辑随着聚合接口越来越多网关会变得越来越重性能和稳定性反而下降。我在项目里的处理方式是不要把所有聚合逻辑都塞进核心网关。网关只做路由和基础横切逻辑复杂聚合逻辑放到 BFFBackend for Frontends层也就是单独为某类客户端提供一个服务端由它去调用下游服务并组装数据。BFF 和网关是两回事BFF 是业务后端网关仍然是入口两者配合。另外网关如果是基于 Spring Cloud Gateway 这种异步非阻塞模型千万别在过滤器里做长时间的同步逻辑比如直接查数据库或者调用外部 API否则会把 event loop 线程卡死网关吞吐量断崖式下跌。3.3 网关如何做动态路由微服务架构下网关不能再靠配置文件里写死的上游地址而是要接入注册中心按服务名动态拉取实例列表再转发请求。这样服务扩缩容、实例上下线时网关无需重启也能自动感知。配置方式大致如下spring: application: name: gateway-service cloud: gateway: discovery: locator: enabled: true lower-case-service-id: true routes: - id: order-service uri: lb://order-service predicates: - Path/api/order/**这里的lb://order-service表示通过注册中心负载均衡地转发到名为 order-service 的服务。路由规则不要写得太复杂尽量保持简单清晰太细的路由匹配条件会增加排障难度。网关上线后一定要有降级预案这个组件一旦挂了整个系统入口就断了所以网关实例要保证多副本并且把健康检查、限流和降级策略都提前配好。4. 配置管理模式配置和代码分离运行期可动态调整4.1 为什么微服务一定要集中配置微服务实例少则十几个多则几十上百如果配置还写在每个服务本地的 application.yml 里改一个数据库地址就要把所有实例一个个发布一遍工作量失控。配置管理模式的核心理念是把配置从代码和部署包中剥离出来集中存储在一个配置中心服务启动时从配置中心拉取运行期还能监听配置变化并动态刷新。这样团队改配置不需要重新发布也不需要登录每台服务器一个操作就能批量生效。最常见的实现有两种Spring Cloud Config 和 Nacos Config。Spring Cloud Config 是个老牌组件基于 Git 存储配置天然有版本管理和审计功能Nacos 更贴近国内使用习惯配置存储、注册中心一套搞定控制台也简单。我自己的习惯是如果项目已经在用 Spring Cloud Alibaba直接选 Nacos 当配置中心少一个依赖和运维组件如果项目是自建 Git 仓库、对配置审计要求高用 Spring Cloud Config 也不差。这里没有唯一正确答案关键是要有“配置中心”这个抽象而不是把配置文件散落在各处。4.2 配置分类与动态刷新的实操细节配置不是一股脑全放配置中心我一般把配置分成两类一类是启动必需配置比如数据源地址、注册中心地址这类配置必须在服务启动前就知道如果放配置中心服务连不上配置中心就会启动失败所以更适合放在本地配置或环境变量里兜底另一类是运行期可变配置比如限流阈值、功能开关、外部接口超时时间这类配置放配置中心才能真正发挥动态刷新的价值。Nacos 下发的配置写法很简单指定服务名和数据源即可spring: cloud: nacos: config: server-addr: 127.0.0.1:8848 file-extension: yml namespace: dev group: DEFAULT_GROUP refresh-enabled: true但注意配置中心推送新值不代表所有 Bean 都会自动生效。用Value注入的字段往往需要配RefreshScopeConfigurationProperties类也要确认支持刷新。我踩过的坑是配置下发后控制台显示已发布业务却还是旧逻辑排查半天发现某个服务没加RefreshScope。这类问题很普遍建议在项目里加一个“配置变更验收”页面能看到当前每个服务实际生效的配置值和最后刷新时间比纯靠日志靠谱得多。4.3 配置的安全管理配置中心里往往会存数据库密码、密钥、第三方 Token 这类敏感信息千万不要明文存放。至少要做到两层一是敏感配置使用加密插件或密钥管理系统存储应用启动时解密密文二是配置中心的访问权限要收敛只有运维和特定开发人员能改生产环境配置。配置中心权限失控比代码泄露更危险因为你改一个阈值可能直接让所有生产实例限流。另一个容易被忽视的问题是配置回滚。有一次我们改了生产环境的缓存过期时间改完发现命中率下降严重还好 Nacos 里保留了历史版本一键回滚才快速恢复。所以我在团队里立了一个规矩任何生产配置变更都要带上变更说明配置中心历史版本至少保留 30 天每次变更后观察监控指标再离开别改完就关电脑。5. 容错模式微服务必须学会“软失败”5.1 熔断模式别让故障蔓延分布式调用里一个服务慢并不可怕可怕的是它把慢传染给所有调用方。比如商品服务依赖库存服务库存服务响应从 20ms 涨到 2s商品服务的线程池就会逐渐被这个慢调用占满后台对商品服务的所有请求也跟着排队最后整个应用线程池耗尽故障像多米诺骨牌一样向上下游扩散。熔断模式就是用来解决这个问题的当失败率超过阈值熔断器直接打开后续请求不再发起真实调用而是快速失败让服务有时间恢复。熔断器一般有三种状态关闭、打开、半开。关闭时正常放行连续失败超过阈值后进入打开状态请求直接短路经过一段冷却时间进入半开状态放少量请求试探如果成功就慢慢恢复关闭如果失败则再次打开。这个行为很像家里的保险丝电流过大就跳闸而不是让电线一直烧下去。Resilience4j 里配置一颗熔断器的示例resilience4j.circuitbreaker: instances: inventoryService: slidingWindowSize: 10 failureRateThreshold: 50 waitDurationInOpenState: 10s minimumNumberOfCalls: 5这里的含义是在最近 10 次调用中至少有 5 次调用且失败率达到 50% 时熔断打开 10 秒后进入半开试探。参数不要照抄要按服务的重要程度和下游恢复时间估算但有一点通用失败阈值不应设置过小否则偶发超时就会频繁熔断造成可用性反而下降。5.2 舱壁隔离与降级把故障控制在一个小范围熔断解决的是“不去调用坏掉的依赖”舱壁隔离解决的则是“即使一个依赖很慢也不能占用所有线程”。舱壁模式Bulkhead会把不同下游服务的调用隔离到独立的线程池或信号量里比如支付调用最多用 10 个线程库存调用最多用 8 个就算库存服务慢到极限也只占满库存那 8 个线程支付链路仍然畅通。用生活化的比喻就是船上有很多舱室一个舱进水了把门关上船不会整个沉掉。降级模式是容错里的兜底策略核心思想是“失败时返回一个可接受的替代结果”。比如获取库存失败时返回“库存未知”而不是直接报错让前端白屏获取推荐列表失败时返回空列表或缓存数据。使用降级要提前定义策略fallback 方法本身要尽量简单如果 fallback 里还去查数据库、调外部接口那失败时照样会拖垮线程等于没降级。另外要注意降级不等于掩盖问题所有降级路径都要有监控和告警一旦触发频繁说明系统已经出问题了不能等用户投诉才知道。5.3 重试与限流别把重试做成雪崩放大器重试是服务稳定性的一把双刃剑。合理的重试能解决瞬时网络抖动但盲目重试会放大流量。一个请求在下游某台实例超时后换成另一台重试看起来没问题但如果整个下游都处于高负载状态客户端重试只会让情况更糟。我见过一个接口失败率 5%因为两层重试导致实际流量放大了将近十倍直接把数据库拖死。所以重试一定要有次数上限还要配指数退避加随机抖动比如第一次等 100ms第二次等 200ms第三次等 400ms不要三毫秒内连打三次。限流和重试是配合使用的关系。限流模式本质是“在流量超过系统处理能力时主动拒绝部分请求”常用的算法有令牌桶、漏桶和滑动窗口。网关层可以做总入口限流保护整个系统不被突发流量压垮每个服务也可以做资源级限流防止某个调用方异常调用拖垮自己。Sentinel 是比较成熟的限流框架支持 QPS 阈值、线程数限制、热点参数限流规则可以动态调整。限流阈值不是拍脑袋定的要压测得到服务的真实容量再按毛利率留 20% 的余量线上随时根据峰值调整。6. 数据一致性模式没有强事务也要有最终一致6.1 微服务为什么不能只靠分布式事务拆分以后最痛苦的问题就是数据。原来一个数据库里一条 SQL 更新订单和库存现在订单服务、库存服务各有自己的库跨库事务成了难题。很多团队第一反应是引入分布式事务框架用两阶段提交2PC来保证强一致。但 2PC 在微服务场景下代价很高协调者锁定资源时间很长任何参与者出问题都要整个事务回滚性能和吞吐量都会被打穿在高并发场景基本不可用。微服务架构更提倡 BASE 理论即基本可用、软状态、最终一致。也就是说不要求所有节点在同一时刻看到一致的数据而是允许数据短暂不一致只要最终一致就可以。这个思路听起来很理想化落到代码上就需要一套明确的数据一致性模式。所谓 Saga 模式就是把一个跨服务的长事务拆成一组本地事务每个本地事务有对应的补偿操作一旦某一步失败就逆序执行补偿把前面已经完成的操作全部回滚。这样避免了全局锁每个服务仍然只保证自己本地事务的原子性。6.2 Saga 的两种编排方式事件驱动与合作式Saga 第一种实现方式是编排式Choreography也就是让服务之间通过消息事件协作。比如用户下单订单服务先创建订单发布“订单已创建”事件库存服务订阅事件后扣减库存再发布“库存已扣”事件支付服务订阅后执行扣款发布“支付成功”事件。如果库存扣减失败或者支付失败通过事件逐个触发补偿动作。这种模式没有中心节点服务之间解耦很彻底但缺点是一旦链路变长事件流会非常难追踪你很难一眼看出当前整个事务走到哪一步。第二种是协调式Orchestration由一个 Saga 协调器统一编排流程类似一部戏的导演。协调器告诉订单服务“创建订单”收到成功反馈后再告诉库存服务“扣库存”再告诉支付服务“扣款”哪一步失败就执行哪一步的补偿。这种模式流程清晰、排障容易但协调器本身成了单点和复杂逻辑集中地。我个人在项目里更倾向协调式因为可运维性更重要尤其是多团队协作时流程状态集中管理能避免“事件满天飞没人说得清当前状态”的尴尬。6.3 事务消息与幂等控制最终一致性的工程实现Saga 只是思路真正落地还需要可靠的异步消息机制。实践中常见的做法是本地消息表或事务消息。RocketMQ 的事务消息是个不错的实现生产者先发送半消息业务代码本地执行事务并提交再确认消息可投递如果中途宕机消息队列会通过回查机制确认事务结果保证本地事务和消息投递的最终一致。这套机制避免了“先改库再发消息”时消息丢失的问题比本地消息表省去了一张表是更现代的方案。但不管用哪种方案消费者侧都必须做幂等。消息可能重复投递消费者处理时必须能识别“这条消息我已经处理过了”否则库存扣两次、订单状态重复流转最终一致就变成了最终混乱。我的习惯是给核心业务消息加全局唯一消息 ID并在消费端用去重表或者 Redis SetNX 做幂等判断处理过后打上标记重复消息直接丢弃。这才算把 Saga 真正落到实处。需要提醒的是不要什么事都用 Saga能用本地事务解决的业务绝不跨服务绕一圈大事务能拆就拆拆不掉再考虑。7. 实践中的典型问题与排查建议7.1 一张速查表帮你定位微服务“常见病”微服务排障和单体很不一样问题往往不在一台机器上而是一条调用链上。下面这些是我在项目里最常遇到的几类现象和排查方向可以直接拿来当参考现象可能原因排查步骤服务间歇性超时线程池耗尽、下游慢、网络抖动查看磁盘、CPU、线程池活跃数结合链路追踪定位慢调用路由找不到服务服务未注册、健康检查误判、客户端缓存旧地址在注册中心看服务列表检查心跳与健康检查状态配置修改不生效RefreshScope缺失、配置分组/命名空间不匹配查配置中心下发日志对比各实例实际配置调用失败但单体正常重试无上限、负载均衡权重不均看调用链重试次数检查负载均衡策略和超时配置Saga 补偿不执行补偿事务未注册、消息投递失败查看协调器状态、消息队列消费进度确认补偿逻辑幂等微服务排障还有个基本功就是必须有链路追踪。一次跨服务请求记录 Trace ID下游日志都带上同一个 ID前端的报错直接按 ID 查全链路否则多个服务日志根本对不上时间线。链路追踪模式虽然没列进“6种模式”里但我个人认为它是微服务落地的隐形基础设施建议在第二步就同步引入。7.2 落地顺序不要一上来就上全套最后聊一下落地顺序。我看到很多团队一上来就把 Nacos、网关、Sentinel、Seata、SkyWalking 全配上项目还没跑起来光依赖就装了一堆出了问题根本不知道是哪一环。我的建议是分步走第一阶段先做服务拆分和服务发现用模块化单体验证边界用小范围服务试水第二阶段再上网关和集中配置第三阶段引入熔断、限流、降级和链路追踪最后当确实出现分布式事务问题时再引入 Saga 或事务消息。每一步都要能通过监控数据和用户体验证明收益否则就是为了微服务而微服务。具体到技术选型学习或者中小团队起步可以直接看 Spring Cloud Alibaba 结合 Nacos 的脚手架很多开源项目比如若依微服务 plus 已经把权限、认证、监控这些常用能力封装好了拿来跑一遍能省很多绕路时间。用 IDEA 搭建微服务也不是难事关键不是搭出来而是搭出来以后你想验证哪种模式、解决什么问题带着问题去学比跟着教程敲一遍代码有用得多。微服务所有的模式都只是工具真正的难点在于判断什么时候用、怎么取舍这个能力只能靠实际踩坑积累。