前言:
好的,今天我们进入这个系列的第三篇。
第一篇我们聊了"为什么需要微服务"以及它带来了哪些挑战。如果把第一篇比作"心法",那今天这篇就是"兵器谱"------我们要把微服务架构里最核心的几件兵器(组件)逐一掰开来看。
为了避免枯燥的"说明书式"罗列,这篇我会先挨个剖析每个组件的本质 ,然后在最后用一个完整的"用户下单"请求,把所有这些组件串成一条线,让你看到它们到底是怎么协同工作的。
由于我接触的最多的就是阿里生态,所以我这里大部分都是阿里开源的组件但是思想其实都差不多。

组件逐个剖析
服务注册与配置中心:Nacos
它是干什么的?
Nacos在中文里通常读作"纳科斯",它的全称是 Dynamic Naming and Configuration Service。它干两件核心事:
服务注册与发现(通讯录):服务启动时,把自己的IP、端口、健康状态登记到Nacos上;调用方要调用某个服务时,从Nacos查地址。
动态配置管理(遥控器) :把应用的各种配置(数据库连接、开关、阈值)存放在Nacos中,修改配置后无需重启服务即可生效。
为什么需要它?
没有它,服务调用必须写死IP(比如http://192.168.1.5:8080)。在云原生时代,服务随时可能扩容、缩容、故障重启,IP是动态变化的。写死IP等于自杀式架构。有了它,调用方只问"谁叫订单服务",Nacos回答"现在有3个实例在运行",调用方拿去用就行。
有什么替代品?
服务发现替代:Consul(自带健康检查更丰富)、Eureka(Netflix出品,但官方已停止维护,生产慎用)、ZooKeeper(老牌协调者,但AP性能不如Nacos)。
配置中心替代:Apollo(携程开源的专精配置中心,权限和灰度发布极强)、Spring Cloud Config(配合Git,但缺少实时推送能力)。
综合推荐 :国内企业几乎清一色选 Nacos,因为它既是注册中心又是配置中心,且支持CP(一致性)和AP(可用性)模式切换,适配各种场景。
API网关:Gateway(以Spring Cloud Gateway为例)
它是干什么的?
网关是系统的"总门卫"和"交警"。所有外部请求(Web、App、OpenAPI)必须先打到网关。它干三件事:
路由转发 :判断请求路径(如
/order/**),转发给对应的微服务集群。统一鉴权:在这里统一校验JWT Token或OAuth2令牌,不用每个微服务都写一遍鉴权代码。
过滤增强:添加全局请求ID(用于链路追踪)、响应头统一格式化、限流(全局限流)。
为什么需要它?
没有网关,前端需要维护几十个微服务的地址列表,且每个服务都要自己写鉴权和限流。那将是巨大的灾难。网关把所有"横切关注点"(认证、日志、限流)剥离出来,让后端服务专心写业务逻辑。不过在实际中并不是所有限流都放在网关,也可以针对不同的服务进行更细腻度的限流。
有什么替代品?
Java生态:Spring Cloud Gateway(当前事实标准,基于WebFlux,非阻塞)、Zuul 1.x/2.x(旧版,性能不如Gateway)。
高性能代理:Kong(基于OpenResty/Nginx,性能极高,适合大流量)、Nginx(只能做反向代理,缺乏服务发现和动态路由能力,需要配合Lua脚本)。
云原生:Traefik(专为K8s设计,自动监听Ingress)。
综合推荐 :如果你项目是Java Spring Cloud,Spring Cloud Gateway 是最平滑的选择;如果是超高并发,可以考虑 Kong。
客户端负载均衡:Spring Cloud LoadBalancer
它是干什么的?
当"订单服务"从Nacos拿到了"库存服务"的3个实例地址(192.168.1.10:8001,...11:8001,...12:8001)时,具体选哪一个来调用?
负载均衡 就是做这个决策的。Spring Cloud LoadBalancer 默认提供**轮询(Round Robin)、随机(Random)、权重(Weighted)**等策略。
为什么需要它?
这里必须强调一个概念:服务端负载均衡(如Nginx) 和 客户端负载均衡(如LoadBalancer) 的区别。
Nginx是集中式的,所有请求都经过它转发,它成了瓶颈。
LoadBalancer是嵌入在调用方进程里的。订单服务自己就知道所有库存实例,自己决定选谁。这样压力分散,没有中心瓶颈,也更灵活(可以按响应时间动态调整权重)。
有什么替代品?
-
Netflix Ribbon :曾经的王者,但现在进入维护模式,Spring Cloud官方已建议迁移到 Spring Cloud LoadBalancer(它是默认的)。
-
服务端替代:如果你不想用客户端负载均衡,可以配合Kubernetes的Service(ClusterIP)来实现,服务名自动解析为Pod IP,由kube-proxy做转发。
流量控制与熔断降级:Sentinel
它是干什么的?
这是微服务系统的"保险丝"和"减压阀"。它干三件救命的事:
流量控制(限流):瞬间流量暴增(比如秒杀),直接拒绝掉部分请求,防止系统被冲垮(比如设置订单接口QPS上限为1000)。
熔断降级 :如果订单服务调库存服务,库存突然响应超时或报错率达到50%。Sentinel会立即"熔断" ------接下来5秒内,订单服务不再调库存服务,而是直接返回降级数据(比如"库存查询繁忙,请稍后重试"或读取本地缓存)。这避免了线程池被耗尽的"雪崩效应"。
热点参数限流:比如针对特定商品ID(爆款)单独设置限流规则。
为什么需要它?
因为网络不可靠,依赖服务一定会挂。如果没有熔断,订单服务调用库存超时(等待3秒),100个请求进来,线程池就堵死了;接下来连订单自己的查询接口也卡死了,这就是级联故障 。Sentinel存在的意义,就是优雅地接受失败,而不是让失败蔓延。
有什么替代品?
Hystrix(Netflix出品):功能类似,但仅支持线程池隔离和信号量隔离,且停止维护,配置繁琐。
Resilience4j(Hystrix官方替代):轻量级,函数式风格,但缺少实时监控大盘。
Sentinel (阿里出品):国内主流首选。支持动态规则、丰富的流量整形(排队等待、预热)、实时监控控制台,非常适合互联网高并发场景。
分布式事务:Seata
它是干什么的?
这是微服务里最难啃的骨头。Seata(Simple Extensible Autonomous Transaction Architecture)旨在解决跨数据库、跨服务的数据一致性问题。
它提供了三种模式,最常见的是 AT模式(自动补偿):
业务执行前,Seata会解析SQL,记录数据"快照(Before Image)"。
订单服务创建订单、库存服务扣库存、账户服务扣余额。如果全部成功,提交并清理快照。
如果其中任何一步失败(比如余额不足),Seata的协调者(TC)会发送"回滚命令"。各个服务根据快照(After Image)生成逆向SQL,把数据自动恢复成修改前的样子。
为什么需要它?
在单体里,直接用数据库@Transactional注解就行。但在微服务里,订单库、库存库、账户库是三个独立的物理库,数据库本身无法跨库回滚。
如果没有Seata,你必须自己写一堆复杂的"补偿接口"并处理各种异常重试,代码量巨大且极易出Bug。
有什么替代品?
最终一致性(MQ方案) :不追求强一致,而是用RocketMQ/RabbitMQ发送事务消息。订单创建后发消息,库存消费消息扣减。如果扣减失败,发补偿消息取消订单。这是最常用的轻量级替代,性能比Seata好,但数据一致性延迟更高。
TCC模式(手动补偿):Try-Confirm-Cancel。需要自己写三个接口(预扣、确认、取消),侵入性强,但灵活性高。Seata也支持TCC。
XA协议:利用数据库本身对XA的支持(两阶段提交),性能较差,锁资源时间长。
综合推荐 :对一致性要求极高的金融、支付场景,用 Seata AT 或 TCC ;对一致性要求不高的业务(如积分、日志),优先用 消息队列(RocketMQ事务消息),性能最好。
一个贯穿全流程的例子:用户"下单"请求
概念说完了,现在我们模拟一次真实的"用户下单"请求
场景:用户在App点击"立即购买"(商品ID=100,数量=1,总价=299元)。
第1步:抵达网关(预处理)
-
请求
POST /order/create打到 Spring Cloud Gateway。 -
网关拦截请求,解析Header中的JWT Token,调用用户服务校验身份(这一步通常用网关的过滤器完成)。
-
鉴权通过,网关根据路由规则(
/order/**转发到order-service),将请求转发出去。
第2步:调用方获取服务地址(服务发现 + 负载均衡)
-
order-service收到请求后,业务逻辑需要调用inventory-service(扣库存)和account-service(扣余额)。 -
order-service 内置的 Spring Cloud LoadBalancer 向 Nacos 发起询问:"库存服务在哪?" Nacos返回
[192.168.1.10:8080, 192.168.1.11:8080]。 -
LoadBalancer 按轮询策略选中了
192.168.1.10:8080。
第3步:开启分布式事务(Seata 开端)
-
order-service 准备发起调用前,向 Seata Server(TC) 申请开启一个全局事务,Seata 生成一个全局唯一的
XID(比如192.168.1.1:8091:123456789)。 -
这个
XID会随着 HTTP Header(或者RPC上下文)透传给库存服务和账户服务。
第4步:执行核心业务(涉及容错保护)
-
订单服务在自己的库里插入订单记录(状态:待支付)。
-
订单服务 通过 Feign/HTTP 调用 库存服务 (扣减库存)。库存服务接收到
XID,执行本地SQLupdate stock set count=count-1 where id=100,但先不提交(Seata 拦截,记录回滚日志)。 -
订单服务 调用 账户服务 (扣余额)。账户服务执行 SQL
update account set balance=balance-299 where user_id=1,同样先不提交。
关键时刻(Sentinel介入) :
假设此时 库存服务 因为数据库连接池满了,响应变得极其缓慢(超过 2 秒)。
订单服务内的 Sentinel 监测到调用库存服务的耗时超过阈值且错误率上升。Sentinel 立即熔断 ------订单服务不再继续傻等库存响应,而是快速失败(降级),直接抛出"库存扣减超时"异常。
第5步:分布式事务回滚(Seata 收尾)
-
因为 Sentinel 触发了熔断,业务代码抛出异常,流程走不下去。
-
Seata(TC) 感知到全局事务执行失败,立即向所有已参与的服务(订单、库存、账户)下发"回滚"指令。
-
各个服务根据之前记录的"修改前快照(Before Image)"生成补偿 SQL。
-
库存服务:
update stock set count=count+1 where id=100(加回库存) -
账户服务:
update account set balance=balance+299 where user_id=1(退回余额) -
订单服务:删除刚才插入的待支付订单。
-
-
数据完美恢复原状,整个分布式事务回滚成功。
第6步:优雅响应与监控
-
订单服务最终返回给网关一个 Json:
{"code":500, "msg":"库存扣减繁忙,请稍后重试"}。 -
网关将响应返回给App,用户看到友好的错误提示。
-
在整个过程中,所有组件(网关、订单、库存、账户)的日志、耗时、调用链全部上抛给 SkyWalking/Prometheus,运维在监控大盘能清楚看到"库存服务"是故障点,立即报警扩容。
总结与思考
通过这个例子,你应该清晰感受到了各个组件的分工哲学:
| 组件 | 扮演角色 | 一句话精髓 |
|---|---|---|
| Nacos | 通讯录 + 遥控器 | 解决"人在哪"和"配置怎么改" |
| Gateway | 总门卫 | 解决"谁能进"和"往哪走" |
| LoadBalancer | 分拣员 | 解决"具体找哪个人干活" |
| Sentinel | 保险丝 | 解决"扛不住时怎么办"(保护自己) |
| Seata | 后悔药 | 解决"砸了摊子怎么复原"(数据一致性) |
"每个组件都是为了解决微服务引入的某一个特定副作用(分布式复杂性)"。
服务拆分了,地址找不到?→ 诞生了 Nacos。
服务拆分了,请求量分散不均?→ 诞生了 LoadBalancer。
服务拆分了,入口太多难管理?→ 诞生了 Gateway。
服务拆分了,网络调用不可靠导致雪崩?→ 诞生了 Sentinel。
服务拆分了,数据库事务无法跨库回滚?→ 诞生了 Seata。
总结
这篇文章总共介绍了服务注册与发现,分布式事务,熔断降级限流,网关,负载均衡,后续会针对每一个部分更加深入的探索。同时我会结合我自己的项目设计进行介绍。