SpringCloud 面试
SpringCloud 面试
综合
【中等】Spring Cloud 有哪些核心组件?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:SpringCloud / 生态总览
💎 关键结论
Spring Cloud 核心组件按职责分九类:注册发现(Eureka/Consul/Nacos)、负载均衡(LoadBalancer)、配置中心(Config/Apollo/Nacos)、服务调用(OpenFeign)、熔断限流(Sentinel/Resilience4j)、网关(Gateway)、消息驱动(Stream)、链路追踪(Sleuth 系)、监控(Boot Admin)。国内主流组合已切换为 Spring Cloud Alibaba:Nacos + Sentinel + Gateway + OpenFeign。
⚡记忆卡片
- 口诀:注册配置调网关,熔断追踪加监控
- 关键词:服务治理 / 流量控制 / Netflix 停更 / Alibaba 替代
- 链路:请求 → 网关 → 注册中心寻址 → 负载均衡 → OpenFeign 调用 → 熔断兜底
📖 核心知识
Spring Cloud 的核心组件主要分为以下几类:
- 服务注册与发现:Eureka、Consul、Nacos
- 负载均衡:Ribbon(停更)、Spring Cloud LoadBalancer
- 配置中心:Spring Cloud Config、Apollo、Nacos
- 服务调用:Feign(停更)、OpenFeign(声明式 HTTP 客户端)
- 熔断与限流:Hystrix(停更)、Resilience4j、Sentinel
- 网关:Zuul(停更)、Spring Cloud Gateway
- 消息驱动:Spring Cloud Stream(整合 Kafka、RabbitMQ 等)
- 链路追踪:Spring Cloud Sleuth(整合 Zipkin、Jaeger)
- 监控与管理:Spring Boot Admin、Spring Cloud Bus
总结来看,Spring Cloud 的核心组件就是围绕:服务治理、配置管理、流量控制、链路追踪、网关、消息驱动、监控 这几大类来解决微服务的一些问题的。
注意,Spring Cloud 第一代 Netflix 组件大多已停止维护,现在公司用 Spring Cloud Alibaba 的会多一些:Nacos(注册 + 配置)+ Sentinel(熔断限流)+ Gateway(网关)+ OpenFeign(调用)。
一次真实请求的全链路
- 用户请求进来,先过 Spring Cloud Gateway 网关,网关查路由、做认证,还用 Sentinel 限流熔断,防流量高峰。
- 网关从 Nacos/Eureka 查服务地址,用 LoadBalancer 挑一个均衡的实例。
- 服务间调用用 OpenFeign,写接口像本地调用,底层带重试。
- 配置从 Nacos Config 拉,变化时 Spring Cloud Bus 广播刷新所有实例。
- 如果下游服务慢或挂,Resilience4j/Sentinel 熔断,返回默认结果。
- 非实时任务丢到 Spring Cloud Stream(用 Kafka),异步处理解耦。
- 全链路用 Sleuth + Zipkin/Jaeger,问题时看日志链。
- Spring Boot Admin 监控集群健康,指标报警。
🔀 发散问题
- Q:Spring Boot 和 Spring Cloud 是什么关系? → Boot 是单服务开发框架,Cloud 是集群治理套件,Cloud 依赖 Boot 运行,见本文档「Spring Boot 和 Spring Cloud 之间的区别?」。
- Q:SpringCloud 与 SpringCloud Alibaba 怎么选? → 新项目首选 Alibaba 全家桶,组件活跃度高,见本文档「SpringCloud 和 SpringCloud Alibaba 有什么区别?」。
- Q:Netflix 组件停更后有哪些替代方案? → Eureka→Nacos、Hystrix→Sentinel/Resilience4j、Zuul→Gateway、Ribbon→Spring Cloud LoadBalancer,见本文档「Spring Cloud 有哪些注册中心?」。
【中等】Spring Cloud 的优缺点有哪些?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:SpringCloud / 技术选型
💎 关键结论
Spring Cloud 的优点是生态完整、基于 Boot 自动配置集成简单、社区强大、组件可替换;缺点是技术栈锁定 Java、Boot 与 Cloud 版本兼容复杂、分布式运维成本高、自动配置隐藏细节导致排障难,且 Hystrix、Zuul 1.x 等部分组件已停止更新,迁移需改造。
⚡记忆卡片
- 口诀:生态全、集成快;版本坑、运维贵
- 关键词:开箱即用 / 版本兼容 / 技术锁定 / 组件停更
- 链路:选型 → 评估生态完整性 → 评估版本兼容 → 评估运维成本 → 决策
📖 核心知识
优点
- 生态完整:提供微服务全栈组件,开箱即用。
- 开发高效:基于 Spring Boot 自动配置,集成简单。
- 社区强大:文档丰富,问题易排查。
- 技术统一:与 Spring 生态无缝融合,学习成本低。
- 组件灵活:支持替换 Netflix 套件(如 Nacos、Resilience4j)。
缺点
- 技术栈锁定:深度绑定 Java/Spring,不适合多语言异构系统。
- 版本兼容复杂:Spring Boot 与 Cloud 版本需严格匹配,升级风险高。
- 运维成本高:微服务组件需独立部署维护,分布式复杂性未降低。
- 性能开销:组件间调用、代理等引入额外延迟和资源消耗。
- 排障难度大:自动配置和动态代理隐藏细节,问题定位需深入底层。
- 组件维护状态:部分组件(如 Hystrix、Zuul 1.x)已停止更新,迁移需改造。
🔬 扩展知识
详情
- 【L3】Spring Cloud 与 Spring Boot 的版本号是两套体系:Boot 用数字版本(2.x/3.x),Cloud 早期用站名(Hoxton、2020.0.x),2020 年后统一为
年份.小版本,升级时必须对照官方版本兼容矩阵。 - 【L3】多语言异构场景下,Spring Cloud 的替代路线是服务网格(Istio + Envoy),把治理能力下沉到 Sidecar,语言无关。
- 【L4】Netflix 于 2018 年前后陆续停止维护 Hystrix、Eureka 2.x、Zuul 1.x 等组件,官方推荐 Resilience4j 替代 Hystrix,这也是 Spring Cloud Alibaba 生态在国内兴起的重要原因。
🔀 发散问题
- Q:多语言技术栈该怎么做微服务治理? → 服务网格(Istio/Envoy)将治理下沉到基础设施层,语言无关,见本文档「Spring Cloud 可以选择哪些 API 网关?」。
- Q:版本升级风险如何控制? → 严格对照版本兼容矩阵 + 灰度发布,见本文档「Gateway 如何实现动态路由?」。
【中等】Spring Boot 和 Spring Cloud 之间的区别?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:SpringCloud / 生态总览
💎 关键结论
Spring Boot 是构建单个微服务的开发框架,专注简化配置、快速启动;Spring Cloud 是协调管理微服务集群的治理套件,专注分布式通信与协调。Spring Cloud 依赖 Spring Boot 运行,但 Boot 可以独立使用——一句话:Boot 管"单兵作战",Cloud 管"团队协同"。
⚡记忆卡片
- 口诀:Boot 造零件,Cloud 组机器
- 关键词:开发框架 / 治理套件 / 依赖关系 / 独立可用
- 链路:Boot 快速构建单服务 → Cloud 提供治理能力 → 组合成微服务系统
📖 核心知识
- Spring Boot:快速构建单个微服务的开发框架,专注于简化配置、快速启动。
- Spring Cloud:协调和管理微服务集群的治理套件,专注于分布式系统的通信与协调。
- Spring Cloud 依赖 Spring Boot 运行,但 Spring Boot 可独立使用。
两者不是替代关系而是层次关系:Boot 解决"一个服务怎么快速开发部署",Cloud 解决"一堆服务怎么发现、配置、调用、容错"。
🔬 扩展知识
详情
- 【L3】Cloud 的每个治理组件(如 OpenFeign、Gateway)本质上都是 Boot Starter,靠 Boot 的自动配置机制即插即用,这也是两者必须版本匹配的原因。
- 【L4】Boot 3.x 时代,Cloud 2022.x 起 Sleuth 被 Micrometer Tracing 替代,可观测能力逐步从 Cloud 套件迁移到 Micrometer 统一门面。
🔀 发散问题
- Q:Spring Cloud 有哪些核心组件? → 注册、配置、调用、熔断、网关、追踪、监控九大类,见本文档「Spring Cloud 有哪些核心组件?」。
- Q:微服务架构适合所有项目吗? → 不是,业务简单、团队小时单体更合适,见本文档「单体架构和微服务架构有什么区别?」。
分布式协同
【简单】什么是 Seata?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:12 min | 🏷 标签:Seata / 分布式事务
💎 关键结论
Seata 是开源的分布式事务解决方案,通过 TC(事务协调者)、TM(事务管理器)、RM(资源管理器)三个组件管理全局事务,提供 AT、TCC、Saga、XA 四种模式。它把分布式事务从业务代码中抽象出来,让开发者像用本地事务一样处理跨服务数据一致性。
⚡记忆卡片
- 口诀:三组件管全局,四模式配场景
- 关键词:TC / TM / RM / XID / undo_log
- 链路:TM 开启事务拿 XID → XID 随调用传播 → RM 注册分支 → TM 决议 → TC 协调提交/回滚
📖 核心知识
Seata 是开源的分布式事务解决方案,致力于为微服务架构提供高性能、易用的一站式分布式事务服务。
它通过引入事务协调者(TC)、**事务管理器(TM)和资源管理器(RM)**这三个核心组件来管理全局事务。其核心价值在于将对分布式事务的处理从业务代码中抽象出来,让开发者能像使用本地事务一样处理跨服务的数据一致性问题。
Seata 提供了四种核心的事务模式,以满足不同业务场景的需求:
| 模式 | 核心原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| AT 模式 | 通过解析业务 SQL,自动生成回滚快照(before/after image),记录在undo_log表中,实现二阶段补偿。基于改进后的两阶段提交协议(2PC)。 | 对业务代码无侵入,学习成本低,性能较高。 | 需要数据库额外建立undo_log表,存在短暂的数据不一致窗口(AP 模型)。 | 适用于绝大多数高并发的互联网场景,允许最终一致性。 |
| TCC 模式 | 要求业务系统自行实现三个方法:Try(预留/检查资源)、Confirm(确认提交)、Cancel(补偿回滚)。 | 性能比 AT 模式更高(无全局锁),可灵活控制业务资源。 | 对业务代码侵入性强,开发复杂度高,需处理空回滚、幂等、防悬挂等问题。 | 适用于对性能要求极高,或需对底层资源进行精确控制的场景。 |
| SAGA 模式 | 将长事务拆分为多个有补偿逻辑的本地子事务,通过状态机或事件驱动依次执行。正向失败时,反向调用补偿操作。 | 适合长事务,性能高,模型简单。 | 需自行实现补偿逻辑,无法保证隔离性(需业务层控制)。 | 适用于业务流程长、需要与外部服务交互(如调用支付宝支付后补偿退款)的场景。 |
| XA 模式 | 基于数据库标准 XA 协议实现的两阶段提交,由事务管理器协调资源。 | 强一致性(CP 模型),对业务无侵入。 | 性能低(资源锁持有到二阶段结束),依赖于数据库对 XA 的支持。 | 适用于金融、交易等对数据一致性要求极高、并发量不高的核心场景。 |
核心工作流程
- 开启全局事务:TM 向 TC 申请开启全局事务,TC 返回一个全局唯一的 XID。
- 分支事务注册:XID 在微服务调用链中传播。每个服务(RM)执行业务操作时,会向 TC 注册分支事务。
- 全局提交/回滚:TM 根据业务执行结果,向 TC 发起全局提交或回滚决议。
- 协调分支事务:TC 根据决议,协调所有 RM 完成二阶段提交或回滚(如 AT 模式根据
undo_log补偿)。
实际集成注意事项
- 组件部署:TC 是独立部署的服务端,负责维护全局事务状态。
- 数据表:使用 AT 模式时,每个业务数据库都需要创建
undo_log表。若使用文件/数据库模式存储事务日志,TC 服务端也需对应的global_table、branch_table等表。 - 配置:需要配置 TC 的服务地址、事务分组,并将应用的数据源代理为
DataSourceProxy,以让 Seata 能拦截 SQL。
🔀 发散问题
- Q:四种模式怎么选? → 大部分业务用 AT,高并发交易用 TCC,长事务跨系统用 Saga,强一致用 XA,见本文档「Seata 支持哪些模式的分布式事务?」。
- Q:AT 模式回滚靠什么? → 靠 undo_log 快照生成逆向 SQL,见本文档「Seata 的事务回滚是怎么实现的?」。
【中等】Seata 支持哪些模式的分布式事务?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:Seata / 事务模式
💎 关键结论
Seata 支持四种模式:AT 自动快照回滚、侵入最小,适合大部分业务;TCC 业务自实现 Try-Confirm-Cancel、性能最高,适合高并发交易;Saga 拆长事务为有序子事务加补偿,适合跨系统长流程;XA 基于数据库原生两阶段提交、强一致但性能最差。选型看侵入性、性能、一致性三角。
⚡记忆卡片
- 口诀:AT 自动、TCC 手动、Saga 补偿、XA 锁库
- 关键词:undo_log / Try-Confirm-Cancel / 补偿操作 / XA 协议
- 链路:业务场景 → 评估侵入性/性能/一致性 → 选 AT/TCC/Saga/XA
📖 核心知识
Seata 支持四种事务模式:AT、TCC、Saga、XA。
- AT 模式:通过代理数据源自动管理事务。在业务 SQL 执行前后自动记录数据快照到 undo_log 表,提交时直接提交本地事务,回滚时用快照生成反向 SQL 还原数据。业务侵入最小,加个
@GlobalTransactional注解就行。 - TCC 模式:是两阶段提交的业务层实现。把一个操作拆成 Try 预留资源、Confirm 确认提交、Cancel 回滚释放三步,每一步都要业务代码自己实现。侵入性大,但性能最好,适合高并发场景。
- Saga 模式:是长事务解决方案。把全局事务拆成多个有序的小事务,每个事务配一个补偿操作。某个事务失败了,按相反顺序依次执行补偿操作回滚之前的变更。适合流程长、参与方多的业务。
- XA 模式:基于数据库原生的 XA 协议。一阶段执行 SQL 后 XA prepare,资源一直锁着,等 TC 统一下发 commit 或 rollback。强一致性,但性能最差,适合对一致性要求极高的场景。
| 模式 | 侵入性 | 性能 | 一致性 | 适用场景 |
|---|---|---|---|---|
| AT | 低 | 高 | 最终 | 大部分业务 |
| TCC | 高 | 最高 | 最终 | 高并发交易 |
| Saga | 中 | 高 | 最终 | 长事务、跨系统 |
| XA | 低 | 低 | 强 | 传统迁移、强一致 |
🔬 扩展知识
详情
- 【L3】TCC 三大经典坑:空回滚(Try 未执行但 Cancel 被调用)、幂等(Confirm/Cancel 可能重复调用)、防悬挂(Cancel 先于 Try 到达),通常用事务控制表记录分支状态解决。
- 【L3】AT 模式的全局锁保证了写隔离:一阶段本地提交前需先拿全局锁,避免脏写;代价是锁竞争影响吞吐,这也是 TCC 性能更高的原因。
- 【L4】AT 的 undo_log 回滚是"最终一致 + 可校验"的:回滚前会校验 after image 与当前数据是否一致,若被全局事务之外的操作修改则需人工介入。
🔀 发散问题
- Q:Seata 的三组件如何协同? → TC 协调、TM 定边界、RM 管资源,见本文档「了解 Seata 的实现原理吗?」。
- Q:回滚时数据被别的操作改了怎么办? → AT 靠全局锁防脏写,校验失败转人工,见本文档「Seata 的事务回滚是怎么实现的?」。
【中等】了解 Seata 的实现原理吗?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:12 min | 🏷 标签:Seata / 实现原理
💎 关键结论
Seata 基于改进的两阶段提交:TM 向 TC 开启全局事务拿 XID,XID 随调用链传播,各 RM 注册分支事务并本地提交(记录回滚日志),最后 TM 向 TC 发起全局决议,TC 协调所有 RM 统一提交或回滚。一阶段不阻塞、二阶段异步补偿,是它比传统 XA 性能好的关键。
⚡记忆卡片
- 口诀:一阶段本地提交,二阶段异步决议
- 关键词:XID 传播 / 分支注册 / 回滚日志 / 全局决议
- 链路:TM 开启 → XID 透传 → RM 注册分支 → TM 决议 → TC 协调 → RM 执行
📖 核心知识
Seata 是一款开源的分布式事务解决方案,其实现原理基于改进型的两阶段提交协议,核心目标是让分布式事务的使用体验与本地事务一样简单。
核心组件
- TC (Transaction Coordinator):独立部署的中心控制器,管理全局事务状态,协调分支二阶段提交或回滚。
- TM (Transaction Manager):嵌入业务服务,通过注解标记事务边界,向 TC 发起开启、提交、回滚请求。
- RM (Resource Manager):嵌入数据层,代理数据源,注册分支事务并记录回滚日志,执行 TC 下发指令。
协同:TM 申请开启 → RM 注册分支并上报 → TC 决策并通知 RM 统一提交或回滚。
Seata 工作流程
- 开启事务:TM 向 TC 申请创建全局事务,获取 XID。
- 执行业务:XID 透传,RM 向 TC 注册分支事务;执行数据库操作并写入回滚日志,提交本地事务。
- 全局决策:业务结束,TM 向 TC 发起全局提交或回滚。
- 协调分支:TC 通知所有 RM 执行二阶段操作(提交/回滚)。
- 结果确认:RM 执行完毕并反馈 TC,TC 更新最终状态。
Seata 四种事务模式
- AT 模式:自动代理 SQL,利用
undo_log实现回滚,对业务无侵入。 - TCC 模式:业务需手动实现 Try-Confirm-Cancel 三阶段,资源控制精准,性能高但代码侵入强。
- SAGA 模式:将长事务拆分为有正向和补偿操作的小事务,适合复杂业务流程。
- XA 模式:基于标准 2PC 协议,提供强一致性,但性能较低。
🔬 扩展知识
详情
- 【L3】XID 如何跨服务传播?Seata 通过拦截器把 XID 放入 RPC/HTTP 请求头(如 Feign RequestInterceptor),下游服务从请求头还原 XID 并绑定到自己的分支事务。
- 【L3】TC 高可用方案:集群部署 + 事务日志落数据库(
global_table/branch_table),任一节点宕机后其他节点可接管恢复。 - 【L4】AT 与 XA 的本质差异在锁持有时长:XA 资源锁从 prepare 一直持有到二阶段结束,AT 一阶段即本地提交释放数据库锁,仅保留全局锁记录,故吞吐显著更高。
🔀 发散问题
- Q:回滚具体怎么执行? → AT 用 undo_log 逆向 SQL、TCC 调 Cancel、Saga 逆序补偿、XA 由数据库回滚,见本文档「Seata 的事务回滚是怎么实现的?」。
- Q:四种模式如何选型? → 按侵入性/性能/一致性三维度对照选,见本文档「Seata 支持哪些模式的分布式事务?」。
【中等】Seata 的事务回滚是怎么实现的?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:Seata / 事务回滚
💎 关键结论
四种模式回滚机制各不相同:AT 读 undo_log 前后快照生成逆向 SQL 还原数据;TCC 由 TC 触发各分支的 Cancel 方法释放 Try 预留的资源;Saga 按执行的相反顺序调用已成功子事务的补偿方法;XA 由 TC 通知所有 RM 执行数据库层面的 ROLLBACK。
⚡记忆卡片
- 口诀:AT 快照、TCC 取消、Saga 补偿、XA 库滚
- 关键词:undo_log / Cancel / 补偿方法 / ROLLBACK
- 链路:全局回滚决议 → TC 下发 → 各模式执行对应回滚动作 → RM 上报结果
📖 核心知识
Seata 四种事务模式回滚机制:
- AT 模式:依赖
undo_log表记录数据修改前后的快照。回滚时,RM 读取快照,通过逆向 SQL 将数据还原至原始状态。 - TCC 模式:回滚由业务代码实现。TC 触发全局回滚时,调用各分支的
Cancel方法,撤销Try阶段预留的资源。 - SAGA 模式:每个子事务对应一个补偿操作。回滚时,按执行顺序的相反方向依次调用已成功子事务的补偿方法,恢复初始状态。
- XA 模式:基于标准 XA 协议。TC 决定回滚后,通知所有 RM 执行
ROLLBACK,由数据库自身通过 XA 机制将未提交的事务回滚。
🔬 扩展知识
详情
- 【L3】AT 回滚的脏写校验:回滚前比对 after image 与当前库数据,若被全局事务外的操作改动(快照不一致),说明数据已被污染,需告警人工介入,不能盲目逆向。
- 【L3】TCC 回滚必须幂等:网络抖动可能导致 Cancel 重复下发;同时要防"空回滚"(Try 从未执行)与"悬挂"(Cancel 先到 Try 后到),常用事务控制表记录状态位。
- 【L4】Saga 补偿的方向性:正向操作需有语义等价的逆操作(如"支付"对应"退款"),与外部系统交互时补偿可能失败,需重试队列 + 对账兜底。
🔀 发散问题
- Q:AT 回滚为什么快? → 一阶段已本地提交,回滚只是异步补偿,见本文档「了解 Seata 的实现原理吗?」。
- Q:哪种模式回滚成本最高? → TCC 需业务自实现 Cancel、Saga 需自实现补偿,XA 锁持有最久,见本文档「Seata 支持哪些模式的分布式事务?」。
分布式调度
【简单】Spring Cloud 有哪些注册中心?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:注册中心 / 选型
💎 关键结论
主流注册中心有四个:Eureka(AP、已停更)、Consul(CP、Raft)、Zookeeper(CP、临时节点)、Nacos(AP/CP 可切换、自带配置中心)。国内新项目首选 Nacos,云原生环境可直接用 Kubernetes Service,无需额外部署注册中心。
⚡记忆卡片
- 口诀:Eureka 保可用,Consul 保一致,Nacos 全都行
- 关键词:AP / CP / 心跳保活 / 多数据中心 / K8s Service
- 链路:选型 → 看 CAP 倾向 → 看健康检查方式 → 看是否需配置中心 → 定案
📖 核心知识
Spring Cloud 通过服务发现抽象支持多种注册中心,主流方案有以下几种:
- Eureka:Netflix 出品,AP 模型,通过客户端心跳保活,是早期的 Spring Cloud 标配,目前已进入维护状态。
- Consul:HashiCorp 出品,CP 模型,基于 Raft 协议,内置了健康检查、KV 存储和多数据中心能力。
- Zookeeper:Apache 顶级项目,CP 模型,强一致性的分布式协调服务,通过临时节点实现服务注册。
- Nacos:阿里巴巴开源,支持 AP/CP 动态切换,集服务发现与配置管理于一身,是国内 Spring Cloud Alibaba 生态的首选。
- Kubernetes Service:云原生环境下的特殊方案,利用 K8s 内置的 Service 和 Endpoints 机制实现服务发现,无需额外部署注册中心。
核心对比与选型
| 特性维度 | Eureka | Consul | Zookeeper | Nacos |
|---|---|---|---|---|
| CAP 模型 | AP(可用性优先) | CP(一致性优先) | CP(一致性优先) | AP/CP 可切换 |
| 健康检查 | 客户端心跳 | 多种主动探测 | 会话(Session)保持 | 多种模式 |
| 配置中心 | 无 | 内置 KV Store | 需自研 | 内置 |
| 管理界面 | 简单 UI | 功能完善 | 无 | 功能完善 |
- 新项目选型:国内 Java 技术栈首选 Nacos(功能全,社区活跃);若追求强一致性或有多数据中心需求,可选 Consul。
- 维护中项目:若已有 Eureka 或 Zookeeper,可继续沿用,需注意 Eureka 已停更,Zookeeper 不适合频繁的服务注册场景。
- 云原生环境:可直接采用 Kubernetes Service,简化运维。
🔀 发散问题
- Q:Eureka 的 AP 设计体现在哪? → 心跳保活 + 自我保护,网络分区时宁可保留过期实例也不误删,见本文档「Eureka 的自我保护机制是什么?」。
- Q:Nacos 的 AP/CP 怎么切? → 临时实例走 Distro(AP)、持久实例走 Raft(CP),见本文档「Nacos 的 AP 和 CP 模式如何切换?」。
- Q:服务注册的底层流程是什么? → 注册、续约、同步三步,见本文档「Spring Cloud 如何实现服务注册?」。
【简单】什么是 Eureka?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:注册中心 / Eureka
💎 关键结论
Eureka 是 Netflix 开源的服务注册发现组件,分 Server(注册表维护)和 Client(注册/心跳/拉取缓存)两端,采用 AP 模型与自我保护机制。2.0 版本已停止开发,现仅建议存量项目沿用,新项目优先考虑 Nacos 或 Consul。
⚡记忆卡片
- 口诀:30 秒心跳,90 秒剔除,分区自保
- 关键词:Server / Client / AP / 自我保护 / 停更
- 链路:Client 注册 → 心跳续约 → 消费者拉取缓存 → 本地负载均衡调用
📖 核心知识
Eureka 是 Netflix 开源的服务注册与发现组件,曾是 Spring Cloud 的核心。
- Server:注册中心,接收服务注册、维护心跳、剔除失效实例。
- Client:内嵌在服务中,负责注册、心跳续约(默认 30 秒)、拉取注册表并缓存(默认 30 秒更新)。
- 关键机制:AP 模型(可用性优先)、自我保护(网络异常时不轻易剔除实例)。
- 现状:2.0 已停止开发,仅建议现有项目沿用,新项目优先考虑 Nacos 或 Consul。
一句话:Eureka 是经典的服务发现框架,采用 AP 设计和自我保护机制,现已进入维护期。
🔀 发散问题
- Q:Eureka 内部是怎么工作的? → 注册发现、心跳续约、服务剔除三大机制,见本文档「Eureka 的实现原理说一下?」。
- Q:网络抖动时会误删实例吗? → 不会,自我保护机制会暂停剔除,见本文档「Eureka 的自我保护机制是什么?」。
【中等】Eureka 的实现原理说一下?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:注册中心 / Eureka
💎 关键结论
Eureka 原理拆成三个机制:服务注册发现(Client POST 元数据到 Server 内存注册表,消费者拉取并本地缓存)、心跳续约(30 秒一次 PUT,刷新 Lease 时间戳)、服务剔除(90 秒无心跳标记过期,后台任务每 60 秒扫描清理,但自我保护触发时暂停剔除)。
⚡记忆卡片
- 口诀:注册靠 POST,保活靠 PUT,剔除看 Lease
- 关键词:registry / Lease / lastUpdateTimestamp / 双层 Map / 本地缓存
- 链路:实例启动 → 注册元数据 → 30s 心跳 → 超时 90s 标记过期 → 60s 扫描剔除
📖 核心知识
Eureka 的实现原理可以拆成三个核心机制来看:服务注册发现、心跳续约、服务剔除。
- 服务注册发现:服务实例启动后,Eureka Client 会向 Eureka Server 发送一个 POST 请求,把自己的元数据注册上去,包括服务名、IP、端口、健康检查地址这些信息。Server 把这些信息存到内存里的注册表 registry 中。服务消费者从 Server 拉取服务列表,缓存到本地,之后的服务调用就基于这份本地缓存做负载均衡选择实例。
- 心跳续约:注册完成后,Client 每隔 30 秒向 Server 发送一次心跳,本质上是个 PUT 请求。Server 收到后更新这个实例 Lease 中的 lastUpdateTimestamp,表示它还活着。心跳机制保证了注册表里的数据是相对新鲜的。
- 服务剔除:如果 Server 连续 90 秒没收到某个实例的心跳,就会把它标记为过期。Server 有个后台任务,每 60 秒扫一次,把过期的实例从注册表里删掉。不过如果触发了自我保护机制,剔除动作会被暂停。
🔬 扩展知识
详情
- 【L3】Eureka Server 集群是 P2P 对等复制:注册信息通过增量同步在各 Server 间传播,不依赖第三方存储,这也是它 AP 特性的来源——分区时各节点数据可能短暂不一致但都可读写。
- 【L3】客户端三级缓存设计:Client 维护 registry 缓存 + fetchRegistry 定时拉取(默认 30 秒),即使 Server 全挂,消费者仍能用本地缓存继续调用,这是可用性优先的具体体现。
- 【L4】为什么心跳间隔 30 秒、剔除阈值 90 秒?这是可用性与时效性的折中:间隔太短网络开销大,太长则故障实例剔除慢,调用方会多次重试失败才切走。
🔀 发散问题
- Q:自我保护触发后注册表会脏到什么程度? → 过期实例不会被删,调用方可能拿到不可达实例,靠客户端重试兜底,见本文档「Eureka 的自我保护机制是什么?」。
- Q:和 Nacos 的推空保护有何异同? → 思路一致,都是防止注册表被误清空,见本文档「Nacos 的 AP 和 CP 模式如何切换?」。
【中等】Spring Cloud 如何实现服务注册?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:注册中心 / 服务注册
💎 关键结论
服务注册三步接入:加注册中心 Starter 依赖、配置注册中心地址与 spring.application.name、启动类加 @EnableDiscoveryClient。底层机制统一为:启动时上报服务名/IP/端口元数据(存双层 Map),之后定时心跳续约,集群节点间互相同步注册表。
⚡记忆卡片
- 口诀:依赖配置加注解,注册续约再同步
- 关键词:Starter / spring.application.name / @EnableDiscoveryClient / 心跳续约 / 双层 Map
- 链路:引入依赖 → 配置地址与服务名 → 启用注解 → 启动注册 → 心跳续约 → 集群同步
📖 核心知识
核心实现步骤
无论使用哪种注册中心,在服务提供者端的实现逻辑基本一致:
- 添加依赖:在项目的
pom.xml中引入对应注册中心的 Starter 依赖。 - 配置地址:在
application.yml或bootstrap.yml中指定注册中心的地址,并配置服务名(spring.application.name)。 - 启用注解:在启动类上添加
@EnableDiscoveryClient或特定注册中心的注解(如@EnableEurekaClient),以开启服务注册与发现功能。
主流注册中心的实现差异
不同注册中心在具体实现上略有不同,主要体现在依赖和配置项上。
| 注册中心 | 核心依赖 | 关键配置项 (示例) | 启用注解 |
|---|---|---|---|
| Eureka | spring-cloud-starter-netflix-eureka-client | eureka.client.serviceUrl.defaultZone=http://... | @EnableEurekaClient |
| Nacos | spring-cloud-starter-alibaba-nacos-discovery | spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 | @EnableDiscoveryClient |
| Consul | spring-cloud-starter-consul-discovery | spring.cloud.consul.host=127.0.0.1 spring.cloud.consul.port=8500 | @EnableDiscoveryClient |
| Zookeeper | spring-cloud-starter-zookeeper-discovery | spring.cloud.zookeeper.connect-string=127.0.0.1:2181 | @EnableDiscoveryClient |
底层工作机制
服务注册不仅是配置,其背后有一套标准的生命周期管理:
- 服务注册:服务启动时,客户端(如 Eureka Client)向注册中心(如 Eureka Server)发起请求,将自己的服务名、IP 地址、端口号等元数据信息注册上去。注册中心将这些信息存储在一个双层 Map 中(第一层 key 是服务名,第二层 key 是实例名)。
- 服务续约:注册成功后,客户端会定期(如默认 30 秒)发送心跳包到注册中心,以告知自身状态正常,这个过程叫“心跳续约”。
- 服务同步:在注册中心集群模式下,一个节点收到注册信息,会将该信息同步给集群中其他节点,保证注册表数据一致。
🔬 扩展知识
详情
- 【L3】
@EnableDiscoveryClient与@EnableEurekaClient的区别:前者是 Spring Cloud Commons 的通用抽象,适用于任意注册中心;后者是 Eureka 专用。Spring Cloud 2020 版本后@EnableEurekaClient已被标记废弃,统一推荐用前者(且引入 discovery starter 后默认自动开启,注解可省略)。 - 【L3】优雅下线:发布时应先调用注册中心的 deregister(或 Nacos 实例权重置 0)+ 等待存量请求处理完再停机,否则消费者缓存里还有旧地址,会打到已关闭的实例上报错。
- 【L4】bootstrap.yml 与 application.yml 的加载时机差异:Nacos 配置中心场景下注册/配置地址通常放 bootstrap,以便在应用上下文初始化前就拉取远程配置。
🔀 发散问题
- Q:下线时消费者还在调用怎么办? → 靠本地缓存过期 + 优雅下线流程规避,见本文档「Eureka 的实现原理说一下?」。
- Q:多环境如何隔离注册信息? → 用 Nacos Namespace 做环境隔离,见本文档「Nacos 中的 Namespace 是什么?」。
【简单】Consul 是什么?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:注册中心 / Consul
💎 关键结论
Consul 是 HashiCorp 出品的服务网格解决方案,最核心的身份是功能全面的服务注册中心:服务发现(DNS/HTTP API 查询)、多种主动健康检查、内置 KV 存储可兼做轻量配置中心。它基于 Raft 协议属于 CP 模型、强一致,这是与 Eureka 的最大区别。
⚡记忆卡片
- 口诀:发现加检查,KV 顶配置,Raft 保一致
- 关键词:服务发现 / 健康检查 / KV 存储 / Raft / 多数据中心
- 链路:服务注册 Consul → 主动健康探测 → DNS/API 查询 → 客户端调用
📖 核心知识
Consul 是由 HashiCorp 公司开发的一款服务网格解决方案,在微服务领域,它最广为人知的身份是一个功能全面的服务注册中心。
它的核心功能主要体现在三个方面:
- 服务发现:作为注册中心,服务启动时向 Consul 注册信息,客户端通过 DNS 或 HTTP API 查询服务地址。
- 健康检查:支持多种主动探测方式(如 HTTP 返回码、TCP 端口、脚本执行),比单纯的心跳机制更可靠,能准确判断服务真实状态。
- KV 存储:内置键值存储,可用于动态配置、协调信息等,部分场景下可替代轻量级配置中心。
在一致性模型上,Consul 采用 Raft 协议,属于 CP 模型,保证了数据强一致性,适用于对一致性要求较高的生产环境。
与 Spring Cloud 集成时,只需引入 spring-cloud-starter-consul-discovery 依赖并配置 Consul 地址即可。
一句话总结:Consul 是一个 CP 模型、功能丰富、支持多数据中心的服务发现与配置工具,其强一致性和完善的健康检查机制是区别于 Eureka 的核心特点。
🔀 发散问题
- Q:心跳保活和主动探测哪个更可靠? → 主动探测能发现"进程活着但服务假死"的情况,心跳做不到,见本文档「Eureka 的实现原理说一下?」。
- Q:CP 模型分区时会怎样? → 少数派节点不可写,优先保一致性,见本文档「Nacos 的 AP 和 CP 模式如何切换?」。
【简单】Nacos 中的 Namespace 是什么?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:Nacos / 配置管理
💎 关键结论
Namespace 是 Nacos 用于环境隔离与多租户的逻辑单元。一个资源由 Namespace + Group + Data ID 三元组唯一定位;默认有 public 空间。典型用法是为 dev/test/prod 各建一个 Namespace,实现同集群多环境隔离。
⚡记忆卡片
- 口诀:命名空间隔环境,三元组定资源
- 关键词:环境隔离 / 多租户 / Namespace+Group+DataID / public 默认空间
- 链路:建 Namespace → 配置 namespace id → 服务/配置归属隔离
📖 核心知识
Nacos 中的 Namespace 是用于环境隔离和资源隔离的逻辑单元,是实现多租户和多环境管理的基础。
它的核心作用如下:
- 环境隔离:通过 Namespace 可以将同一套 Nacos 集群划分为多个相互隔离的逻辑环境。例如,创建
dev、test、prod三个 Namespace,不同环境的服务和配置数据在物理上虽然存储在同一集群,但在逻辑上完全隔离,互不可见。 - 多租户支持:在 SaaS 或企业内部多团队场景中,可以为每个团队或每个租户分配独立的 Namespace。团队 A 只能操作自己的 Namespace,无法感知或修改团队 B 的服务与配置。
- 资源唯一性:在 Nacos 中,一个资源的完整定位由 Namespace + Group + Data ID 三元组唯一确定。不同 Namespace 下可以有相同的 Group 和 Data ID,这为多环境部署相同的配置文件提供了便利。
- 默认 Namespace:Nacos 初始化时会自动创建一个名为
public的默认 Namespace,如果不显式指定,所有服务和配置都会归属于这个公共空间。
在 Spring Cloud 中,可以通过配置 spring.cloud.nacos.discovery.namespace 和 spring.cloud.nacos.config.namespace 来指定服务注册和配置拉取所属的 Namespace。
🔀 发散问题
- Q:配置变更如何通知到客户端? → 长轮询 + MD5 比对 + 本地快照,见本文档「你知道 Nacos 配置中心的实现原理吗?」。
- Q:为什么注册要指定 namespace? → 防止跨环境服务互相发现造成误调用,见本文档「Spring Cloud 如何实现服务注册?」。
【简单】什么是 Hystrix?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:熔断降级 / Hystrix
💎 关键结论
Hystrix 是 Netflix 开源的容错库,三大核心能力:断路器(失败率达阈值自动熔断防雪崩)、资源隔离(线程池或信号量隔离依赖)、降级回退(熔断或异常时执行 Fallback)。目前已停止维护,Spring Cloud 官方推荐 Resilience4j 替代,国内新项目多选 Sentinel。
⚡记忆卡片
- 口诀:熔断隔离加降级,Netflix 已停更
- 关键词:断路器 / 线程池隔离 / Fallback / Resilience4j / 停维
- 链路:调用失败累积 → 断路器打开 → 请求直接 Fallback → 半开探测 → 恢复
📖 核心知识
Hystrix 是 Netflix 开源的容错库,核心功能如下:
- 断路器:失败率达阈值自动熔断,防止雪崩。
- 资源隔离:通过线程池或信号量隔离依赖,避免相互影响。
- 降级回退:熔断或异常时执行预定义的 Fallback 逻辑。
目前 Hystrix 已进入维护状态(停止新功能开发),Spring Cloud 官方推荐替代品为 Resilience4j,国内 Spring Cloud Alibaba 生态则普遍使用 Sentinel。
🔀 发散问题
- Q:Hystrix 和 Sentinel 怎么选? → Sentinel 限流能力更强且活跃维护,新项目选 Sentinel,见本文档「Sentinel 与 Hystrix 的区别是什么?」。
- Q:线程池隔离和信号量隔离的取舍? → 线程池支持超时中断但开销大,信号量轻量但无法中断,见本文档「什么是服务雪崩?如何做全链路防护?」。
【中等】Sentinel 是怎么实现限流的?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:12 min | 🏷 标签:Sentinel / 限流
💎 关键结论
Sentinel 限流的本质是"实时统计 + 规则校验":请求经 SphU.entry() 进入责任链,StatisticSlot 用滑动窗口(LeapArray)统计 QPS/线程数,FlowSlot 比对规则决定放行或抛 BlockException。三种流控效果——快速失败、预热、匀速排队,规则可通过 Nacos 等动态推送。
⚡记忆卡片
- 口诀:滑动窗口统计,责任链校验,三档整形
- 关键词:LeapArray / FlowSlot / BlockException / 预热 / 匀速排队
- 链路:SphU.entry() → NodeSelectorSlot → StatisticSlot → FlowSlot → 放行/BlockException
📖 核心知识
Sentinel 的限流实现基于责任链模式,核心是围绕资源的 Entry 申请过程,通过一系列 Slot 进行规则检查与统计。具体实现分为以下几个核心维度:
限流统计维度和算法
- QPS 限流:采用滑动时间窗口算法。
- 通过
LeapArray结构将时间划分为多个小窗口(如 1 秒拆成 2 个 500ms 窗口),动态统计请求量,实现更精准的流量控制。
- 通过
- 线程数限流:采用信号量隔离机制。
- 统计当前资源的并发线程数,超过阈值则直接拒绝新请求。相比 Hystrix 的线程池隔离,这种方式无额外线程切换开销,适用于控制长耗时操作的并发度。
- 热点参数限流:结合 LRU + 令牌桶算法。
- 统计传入参数中的热点值(如商品 ID),针对特定参数值进行精细化的并发数或 QPS 控制,防止单一热点值打垮系统。
限流效果控制策略
- 快速失败:默认模式,超出阈值直接拒绝,抛出
FlowException。 - 预热模式:冷启动时,流量阈值从
1/3的设定值开始,在预热时长内线性增长到设定值,避免刚启动时被突发流量打垮。 - 匀速排队:严格控制请求通过间隔(如 QPS=100 则间隔 10ms),让请求以均匀速度通过,用于消息队列削峰填谷。
规则管理
- 动态规则配置:支持通过文件、Nacos、Apollo 等数据源动态推送和加载限流规则,无需重启应用。
- 集群限流:通过 Token Server 协调集群中各节点的流量,实现精确的集群整体流量控制。
核心执行流程
- 请求进入
SphU.entry()后,会经过ProcessorSlotChain(责任链):NodeSelectorSlot:为资源构建调用树节点。StatisticSlot:执行实时统计(pass/block 计数)。FlowSlot:根据限流规则(QPS/并发数)判断是否限流。- 若规则触发,抛出
BlockException;否则请求通过,继续执行业务逻辑。
总结:Sentinel 限流的本质是 “实时统计 + 规则校验”,通过滑动窗口统计实时指标,利用责任链模式执行校验,并支持多种流量整形策略来适应不同场景。
🔬 扩展知识
详情
- 【L3】为什么用滑动窗口而不是固定窗口?固定窗口存在临界突变问题:两个窗口交界处瞬时流量可能达到阈值 2 倍。LeapArray 把窗口切细后随时间滚动淘汰旧桶,统计更平滑。
- 【L3】匀速排队底层是漏桶思想,基于请求到达时间与期望通过间隔的差值计算等待时长,超过最大排队时长才拒绝,适合突发流量转平稳消费的场景。
- 【L4】Sentinel 规则默认存内存、重启即丢;生产必须接持久化数据源(Nacos 等),控制台改规则写数据源,客户端监听数据源动态刷新,形成"控制台→数据源→客户端"闭环。
🔀 发散问题
- Q:单机限流怎么扩展到集群维度? → Token Server 集中发令牌 + 客户端批量拉取,见本文档「Sentinel 是怎么实现集群限流的?」。
- Q:限流和熔断的边界在哪? → 限流管入口流量大小,熔断管下游故障隔离,见本文档「Sentinel 是如何实现熔断降级的?」。
【中等】Sentinel 是怎么实现集群限流的?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:Sentinel / 集群限流
💎 关键结论
单机限流无法控制集群总流量(10 台机器各限 100 QPS,总量可达 1000)。集群限流引入 Token Server 作为全局协调者:客户端处理请求前向 Server 申请令牌,按 Server 响应放行或拒绝;Server 不可用时自动退化本地限流兜底,并用批量拉取令牌减少网络开销。
⚡记忆卡片
- 口诀:Server 发令牌,Client 批量领,挂了降级本地限
- 关键词:Token Server / Token Client / 批量拉取 / 本地降级
- 链路:请求到达 → Client 申请令牌 → Server 全局判定 → 放行/拒绝(Server 挂→本地限流兜底)
📖 核心知识
Sentinel 集群限流核心机制:
- 引入 Token Server:独立部署的全局流量协调者,负责维护全局限流状态(如集群 QPS 总量)并做出最终决策。
- Token Client 工作机制:业务节点处理请求时,向 Token Server 申请令牌。若 Server 可用,则根据其响应决定放行或拒绝;若 Server 不可用或超时,自动退化成本地限流规则,保证系统基础可用。
- 性能优化:采用批量拉取策略(如一次申请多个令牌在本地消费),大幅减少网络通信开销。
- 高可用保障:支持多 Server 集群部署,客户端本地限流兜底。
一句话总结:通过 Token Server 集中协调全局流量统计,配合客户端批量拉取和本地降级机制,实现精准的集群总体流量控制。
🔬 扩展知识
详情
- 【L3】Token Server 两种部署形态:独立模式(专用集群)与嵌入模式(指定某业务节点兼任 Server),嵌入模式省资源但故障切换复杂;生产建议独立模式 + 备份节点。
- 【L3】批量拉取的代价是限流精度下降:一次领 50 个令牌本地消费,节点流量突降时令牌浪费,总量会略低于阈值——精度与网络开销的权衡。
- 【L4】Server 宕机切换期间的"降级→恢复"可能造成流量抖动,可配合 Flow 规则的 fallback 本地阈值设计平滑过渡。
🔀 发散问题
- Q:为什么不能用网关统一限流替代集群限流? → 网关在入口层,服务间内部调用流量它看不到,两者层次不同,见本文档「什么是 Spring Cloud Gateway?」。
- Q:限流的统计基础是什么? → LeapArray 滑动窗口,见本文档「Sentinel 是怎么实现限流的?」。
【中等】Sentinel 是如何实现熔断降级的?⭐⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:12 min | 🏷 标签:Sentinel / 熔断降级
💎 关键结论
Sentinel 熔断 = “滑动窗口统计 + 三态状态机(关/开/半开)”:支持慢调用比例、异常比例、异常数三种策略,指标达阈后断开调用快速失败并执行 fallback,熔断时长结束后放行探测请求,成功则恢复、失败重新计时。半开探测是避免永久熔断的关键。
⚡记忆卡片
- 口诀:三策略触发,三态流转,半开探测定去留
- 关键词:慢调用比例 / DegradeException / timeWindow / Half-Open / fallback
- 链路:滑动窗口统计 → 指标达阈 → 熔断打开拒绝请求 → 时长到 → 半开探测 → 恢复或重断
📖 核心知识
Sentinel 的熔断降级(Degrade)基于对资源调用质量的实时统计,当指标达到阈值时断开调用并快速失败,给故障的下游喘息时间,防止级联雪崩。
三种熔断策略
| 策略 | 触发条件 |
|---|---|
| 慢调用比例 | RT 超过阈值的调用占比达到目标值(需配置最大允许 RT 和比例) |
| 异常比例 | 单位统计时长内异常调用占比达到阈值 |
| 异常数 | 单位统计时长内异常数超过阈值 |
熔断状态机
- 关闭(Closed):正常放行,滑动窗口实时统计;统计窗口内达到阈值则转为打开。
- 打开(Open):直接拒绝所有请求,抛
DegradeException并执行 fallback 降级逻辑,持续timeWindow时长。 - 半开(Half-Open):熔断时长结束后放行一个探测请求,成功则恢复关闭,失败则重新熔断并重新计时。
与限流的区别:限流管“进来的流量不能太大”(流量控制),熔断管“有问题的下游不能一直调”(故障隔离)。降级逻辑通过 @SentinelResource(fallback/blockHandler) 或 Feign 的 fallback 实现。
一句话总结:Sentinel 熔断 = “滑动窗口统计 + 三态状态机(关/开/半开)”,半开探测是避免永久熔断的关键。
🔬 扩展知识
详情
- 【L3】源码定位:熔断状态机在
com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker(AbstractCircuitBreaker#tryPass处理半开探测),统计基于LeapArray滑动窗口;对比 Resilience4j 的io.github.resilience4j.circuitbreaker.CircuitBreaker(核心方法acquirePermission/onSuccess/onError)。 - 【L3】半开只探测 1 个请求的缺陷:若恰好打到故障实例会立即重新熔断,造成振荡。可改用 Resilience4j 的
permittedNumberOfCallsInHalfOpenState(建议 5-10 次)降低误判。 - 【L4】慢调用比例的 RT 阈值怎么定?建议取接口 P99 × 1.5;异常比例需配合最小请求数(如 20)防止低流量误判。
🔀 发散问题
- Q:熔断和限流分别防什么? → 限流防入口过载,熔断防下游故障扩散,见本文档「什么是服务雪崩?如何做全链路防护?」。
- Q:Hystrix 的熔断策略有什么不同? → Hystrix 以异常比例为主,无慢调用比例,见本文档「Sentinel 与 Hystrix 的区别是什么?」。
【中等】Sentinel 与 Hystrix 的区别是什么?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:Sentinel / 选型对比
💎 关键结论
核心差异四点:隔离机制——Hystrix 默认线程池隔离成本高,Sentinel 用信号量隔离更轻量;限流能力——Sentinel 支持热点限流、预热、匀速排队等丰富流量整形,Hystrix 基本不具备;系统防护——Sentinel 有自适应系统保护;维护现状——Hystrix 已停更,Sentinel 活跃维护,新项目选 Sentinel。
⚡记忆卡片
- 口诀:信号量轻、限流强、系统保护、还在维护
- 关键词:信号量隔离 / 线程池隔离 / 流量整形 / 自适应保护 / 停更
- 链路:对比隔离 → 对比熔断策略 → 对比限流能力 → 对比维护状态 → 选型
📖 核心知识
| 维度 | Sentinel | Hystrix |
|---|---|---|
| 设计理念 | 流量控制 + 熔断降级 + 系统保护 | 以隔离和熔断为主的容错机制 |
| 隔离策略 | 信号量隔离(并发线程数限流) | 线程池隔离(默认) / 信号量隔离 |
| 熔断策略 | 慢调用比例、异常比例、异常数 | 异常比例为主 |
| 限流能力 | 丰富(热点限流、匀速排队、预热模式) | 有限支持 |
| 系统保护 | 系统自适应保护(Load/CPU) | 不支持 |
| 控制台 | 完善,动态规则配置、实时监控 | 较弱,需自行集成 |
| 活跃度 | 活跃维护(阿里) | 已进入维护状态(Netflix) |
核心差异总结:
- 隔离机制:Hystrix 线程池隔离成本高,Sentinel 信号量隔离更轻量。
- 限流能力:Sentinel 支持丰富的流量整形,Hystrix 基本不具备。
- 系统级防护:Sentinel 提供自适应系统保护,Hystrix 无。
- 生态与现状:Sentinel 持续活跃,Hystrix 已停更,新项目选 Sentinel。
🔬 扩展知识
详情
- 【L3】Hystrix 默认线程池隔离的价值在于支持超时中断(能主动丢弃卡住的调用);代价是每次调用约 1-2ms 上下文切换开销。对数千 QPS 毫秒级 RT 的高频调用,必须改信号量隔离。
- 【L3】Sentinel 选择信号量隔离 + 并发线程数限流的轻量路线,代价是失去超时中断能力,需用慢调用熔断弥补。
- 【L4】非 Java 语言场景下,两者都不适用,需转向服务网格(Envoy/Istio)或 OpenTelemetry 生态的容错方案。
🔀 发散问题
- Q:信号量隔离的代价是什么? → 无法超时中断,只能等调用自身返回,见本文档「什么是服务雪崩?如何做全链路防护?」。
- Q:Sentinel 限流的具体实现? → 滑动窗口 + 责任链,见本文档「Sentinel 是怎么实现限流的?」。
【中等】什么是服务雪崩?如何做全链路防护?⭐⭐⭐⭐
🎯 目标等级:L3 | ⏱ 建议用时:20 min | 🏷 标签:容错体系 / 全链路防护
💎 关键结论
服务雪崩是下游故障导致上游线程阻塞、故障逐级传导、最终整个系统不可用。防护核心是“快速失败 + 资源隔离 + 熔断兜底”:网关限流 → 调用层超时 + 重试(仅幂等)→ 熔断降级 → 隔离 → 系统保护 → 架构层异步解耦,永远不要赌下游是稳定的。
⚡记忆卡片
- 口诀:超时快失败,熔断加兜底,隔离保线程,异步削依赖
- 关键词:级联阻塞 / 超时控制 / 熔断降级 / 隔离 / 系统自适应保护
- 链路:下游故障 → 线程阻塞 → 线程池耗尽 → 逐级传导 → 雪崩;反向:限流→超时→熔断→隔离→异步
📖 核心知识
服务雪崩指微服务同步调用链中,某个下游服务故障(响应慢、宕机)导致上游线程被阻塞等待,故障逐级传导扩散,最终整个系统不可用。
全链路防护手段(纵深防御)
| 层次 | 手段 | 作用 |
|---|---|---|
| 网关层 | Sentinel/RateLimiter 限流、鉴权过滤 | 拦截异常流量,防止过载 |
| 调用层 | 超时控制(OpenFeign/RPC 超时) | 快速失败,防止线程被无限挂起 |
| 调用层 | 重试(仅限幂等接口) | 消除偶发故障 |
| 调用层 | 熔断降级(Sentinel/Resilience4j) | 故障服务停止调用,返回兜底结果 |
| 调用层 | 隔离(线程池/信号量) | 防止单个服务耗尽所有资源 |
| 系统层 | 系统自适应保护(Load/CPU) | 最后一道防线 |
| 架构层 | 异步解耦(MQ)、缓存兜底 | 削弱同步依赖 |
典型组合:网关限流 → OpenFeign 超时 + 重试 → Sentinel 熔断降级 → fallback 返回默认值。注意超时应满足“上游超时 > 下游超时 × 重试次数”,否则重试形同虚设;阈值需通过压测确定,并配合监控告警提前发现。
方案权衡
权衡 1:隔离方式——线程池隔离 vs 信号量隔离
| 维度 | 线程池隔离(Hystrix 默认) | 信号量隔离(Sentinel 并发线程数限流 / Hystrix 信号量模式) |
|---|---|---|
| 隔离强度 | 强,下游拖慢只耗自己池的线程 | 弱,仅控制并发度,不隔离执行线程 |
| 额外开销 | 线程上下文切换,每次调用约 1-2ms | 几乎无 |
| 超时控制 | 支持(池内线程可被中断丢弃) | 不支持,只能等调用自身返回 |
| 适用场景 | 中低频、RT 较高(> 100ms)且必须超时的调用 | 高频低延迟调用(数千 QPS、毫秒级 RT) |
权衡 2:熔断快速失败 vs 降级兜底——熔断只解决“不再调”,降级解决“不调之后返回什么”。快速失败(抛异常交给上层处理)适合上层有补偿逻辑的链路,优点是不掩盖问题;降级兜底(返回默认值/缓存旧值)适合读多写少的展示场景,缺点是可能用陈旧数据掩盖故障,必须配降级次数告警。
权衡 3:熔断策略——异常比例 vs 慢调用比例。异常比例只能拦“报错”,下游假死(不报错但 RT 飙到 5 秒)拦不住;慢调用比例能拦“慢”,但需正确设置 RT 阈值(建议取接口 P99 × 1.5)。生产上两者叠加使用更稳。
阈值经验值
| 项 | 建议值 | 取舍原因 |
|---|---|---|
| 超时 | 下游接口 P99 × 2 | 既不误伤正常波动,又能快速切断故障 |
| 熔断错误率阈值 | 50% | Resilience4j 默认值;过低误熔断、过高切断不及时 |
| 最小调用数 | 20-100 | 防止低流量下统计误判 |
| 熔断时长 | 10-30 秒 | 给下游恢复窗口,过长影响故障恢复速度 |
| 重试次数 | ≤ 2 次且仅幂等接口 | 防流量放大,重试总耗时需纳入上游超时预算 |
| 隔离线程池大小 | 峰值并发 × 1.5 | 既防单个依赖吃光线程,又留缓冲 |
🔬 扩展知识
详情
- 【L3】源码定位:Sentinel 入口是
SphU.entry(),熔断状态机在com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker(AbstractCircuitBreaker#tryPass处理半开探测),统计基于LeapArray滑动窗口;Resilience4j 对应io.github.resilience4j.circuitbreaker.CircuitBreaker(核心方法acquirePermission/onSuccess/onError);Hystrix 则是HystrixCommand#run+HystrixThreadPoolProperties线程池隔离。 - 【L3】Feign 默认 read timeout 是 60 秒(
feign.Request.Options默认值),下游假死时每个请求占用上游线程 60 秒,Tomcat 默认 200 个线程很快耗尽。经验法则:超时 = 下游接口 P99 × 2,且不超过上游剩余时间预算的一半。 - 【L4】故障期间拿到的下游实例地址依赖注册中心的本地缓存与心跳剔除(Nacos/Eureka 内部原理属分布式协同领域,此处一句带过不展开)。
- 【L4】合格降级 fallback 三条件:① 有业务含义(默认值、缓存旧数据、引导页);② 不依赖同一故障源,fallback 链路必须与被熔断的下游物理隔离;③ 可观测,降级次数进监控告警。直接返回 null 会把异常转移给上层或前端,往往引发 NPE 二次事故。
📚 延伸阅读:Sentinel 官方文档 - 熔断降级
🏭 实战场景
详情
踩坑案例:无超时 + 无熔断引发全站雪崩
现象:某晚高峰,交易服务的支付回调接口批量超时,随后订单服务、商品服务依次超时,整个商城站点不可用约 15 分钟。
排查:监控 + jstack 显示上游各服务的 Tomcat 线程全部阻塞在等待支付服务 HTTP 响应上(WAITING);支付服务实际卡在一条慢 SQL(缺索引导致锁等待)。关键发现:所有 OpenFeign 调用都没配超时,走 Feign 默认 60 秒 read timeout,且全程没有熔断。
根因:下游慢查询 → 每个请求占用上游线程 60 秒 → 上游线程池(Tomcat 默认 200)耗尽拒新请求 → 再上游同样耗尽 → 级联雪崩。
修复:① 紧急杀掉支付服务阻塞会话并补索引;② 全链路统一超时:接口超时 = P99 × 2(支付接口 P99 300ms,设 600ms);③ 关键调用挂 Sentinel 慢调用比例熔断(RT 阈值 500ms、比例 50%、最小请求数 10、熔断时长 10 秒);④ 非关键回调改 MQ 异步解耦。修复后做过一次故障注入演练:下游人为注入 5 秒延迟,上游 RT 短暂抖动后由熔断兜住,未再雪崩。
应急场景题:5 分钟内决策止血
场景:凌晨告警:核心订单服务 RT 从 200ms 飙到 8 秒,错误率 30%,上游网关线程池快打满。初步判断是下游库存服务引起,如何在 5 分钟内决策止血?
应急处理(前 2 分钟):① 看监控大盘与调用链,确认最耗时的下游节点就是库存服务;② 不要先重启订单服务(重启丢现场,且重启期间存量请求更惨),直接止血:通过 Sentinel 控制台对库存调用下发熔断规则,或打开预案开关降级为「返回缓存库存/允许超卖事后对账」,先保住主链路可下单。
根因分析(5-30 分钟):查库存服务日志与慢查询,常见根因是促销锁竞争、缺索引、单实例 Full GC;同时确认上游是否开了重试在放大流量,有则立即关闭。
长期方案:① 所有同步调用默认挂慢调用比例熔断 + 预置好的 fallback,不允许裸调用上线;② 超时规范(P99 × 2)纳入发布 checklist,代码评审必查;③ 库存扣减等强依赖评估改 MQ 异步 + 最终一致;④ 定期故障演练(主动注入下游延迟),验证熔断与降级真能触发。
权衡:降级「允许超卖事后对账」本质是牺牲一致性换可用性,前提是业务有对账补偿能力;若业务不允许超卖,fallback 只能返回「系统繁忙」拒单。可用性与一致性的取舍必须提前和业务方约定成预案,而不是凌晨由值班工程师现场拍板。
⚠️ 常见误区
详情
常见误区(失效场景):
- ❌ “不配超时也能跑” → Feign 默认 60 秒 read timeout,下游假死时上游线程很快耗尽,超时 = P99 × 2 是底线。
- ❌ “重试能提高成功率” → 对已故障的下游重试 3 次等于流量放大 3 倍,加速压垮;重试只适合幂等接口 + 偶发网络抖动,且必须与熔断配合。
- ❌ “熔断恢复就安全了” → 半开探测只放行 1 个请求,若恰好打到故障实例会立刻重新熔断,熔断器反复振荡;可增大半开探测次数。
- ❌ “低流量接口也会熔断” → 异常比例需最小调用数支撑(如 Resilience4j
minimumNumberOfCalls=20),低流量时即使全失败也不触发。 - ❌ “一个熔断器包打所有接口” → 一个慢接口熔断后正常接口也被连带熔断;应按接口或下游服务维度拆分资源。
- ❌ “配了 fallback 就万事大吉” → fallback 查的备用库与被熔断的下游在同一机房/同一实例组,故障时一起挂,降级形同虚设。
🔀 发散问题
- Q:Sentinel 的半开状态与 Hystrix 有何不同?熔断器“振荡”怎么破? → 两者半开都只放行少量探测请求(Sentinel 是 1 个),探测成功关闭熔断、失败重新计时。振荡的本质是探测样本太少 + 下游处于不稳定边缘态:可增大半开探测次数、拉长熔断时长、优先用慢调用比例而非单次异常判定,同时修复下游根因才是治本。
- Q:线程池隔离每次调用多一次线程切换,为什么 Hystrix 还默认用它? → 线程池的核心价值是支持超时中断和彻底的资源隔离,适合一般 RPC 容错场景;但对高频低延迟调用,上下文切换开销占比过大,必须改信号量隔离;Sentinel 选择了信号量 + 并发线程数限流的轻量路线,代价是失去超时中断能力,需用慢调用熔断弥补。
- Q:OpenFeign 的超时和重试怎么配才不踩坑? → 超时分级配置 + 重试仅幂等接口,见本文档「OpenFeign 如何配置超时和重试?」。
- Q:网关层怎么做第一道防护? → 限流 + 鉴权 + 熔断过滤器,见本文档「Spring Cloud Gateway 的工作原理是什么?」。
一句话总结:雪崩防护的核心是“快速失败 + 资源隔离 + 熔断兜底”,永远不要赌下游是稳定的。
分布式治理
【简单】Spring Cloud 可以选择哪些 API 网关?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:网关 / 选型
💎 关键结论
主流网关五选:Spring Cloud Gateway(官方主推,WebFlux/Netty 异步非阻塞,Java 栈首选)、Kong(Nginx+OpenResty,性能高但需 Lua)、APISIX(性能相当、多语言插件、etcd 热更新)、Zuul(1.x 同步阻塞已停维,不推荐)、Envoy(C++,适合服务网格)。纯 Java 选 Gateway,极致性能/多语言选 Kong/APISIX,服务网格选 Envoy。
⚡记忆卡片
- 口诀:Java 用 Gateway,性能选 APISIX,网格上 Envoy
- 关键词:WebFlux / Netty / OpenResty / etcd / Zuul 停维
- 链路:技术栈 → 性能要求 → 二开能力 → 选型
📖 核心知识
核心网关对比
- Spring Cloud Gateway:官方主推,基于 WebFlux/Netty 的异步非阻塞网关,与 Spring Cloud 生态(Nacos、Sentinel 等)集成无缝,Java 技术栈首选。
- Kong:基于 Nginx + OpenResty 的高性能网关,单机 QPS 10 万+,插件生态丰富;二次开发需 Lua,Java 开发者门槛较高。
- Apache APISIX:同属 Nginx/Lua 路线,性能和 Kong 相当,支持多语言插件(Java/Go/Python),配置通过 etcd 热更新,国内应用广泛。
- Zuul:Netflix 早期网关,1.x 为同步阻塞模型,性能瓶颈明显,整体已进入维护模式,新项目不推荐。
- Envoy:C++ 底层,CNCF 毕业项目,适合服务网格及多语言架构,二次开发难度大。
选型建议
- 纯 Java 技术栈:首选 Spring Cloud Gateway。
- 极致性能或多语言架构:Kong 或 Apache APISIX。
- 服务网格场景:Envoy 为更优选择。
🔀 发散问题
- Q:Gateway 内部是怎么工作的? → 路由 + 断言 + 过滤器三件套,见本文档「Spring Cloud Gateway 的工作原理是什么?」。
- Q:Zuul 为什么被淘汰? → 1.x 同步阻塞模型性能瓶颈 + Netflix 停维,见本文档「什么是 Spring Cloud Zuul?」。
【简单】什么是 Spring Cloud Gateway?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:网关 / Gateway
💎 关键结论
Spring Cloud Gateway 是基于 WebFlux + Netty 的响应式 API 网关,集路由、断言、过滤于一体:请求进来后断言匹配路由,先走 Pre 过滤器链,转发下游,再走 Post 过滤器链返回。异步非阻塞性能优于 Zuul 1.x,是 Spring 官方主推的入口方案。
⚡记忆卡片
- 口诀:路由定目标,断言判条件,过滤做加工
- 关键词:Route / Predicate / Filter / WebFlux / Netty
- 链路:请求 → 断言匹配路由 → Pre 过滤器 → 转发下游 → Post 过滤器 → 响应
📖 核心知识
Spring Cloud Gateway 是基于 Spring WebFlux 和 Netty 构建的响应式 API 网关,提供异步非阻塞 I/O,是 Spring Cloud 生态的官方入口解决方案,集路由、断言、过滤于一体,兼具高性能与生态集成度,是 Java 技术栈的首选网关。
核心组件
- 路由:基础单元,包含 ID、目标 URI、断言集合和过滤器集合。
- 断言:匹配请求条件(如路径、Header、参数),决定路由目标。
- 过滤器:在请求路由前后执行处理(如鉴权、限流、修改请求/响应)。
工作流程
- 请求进入网关
- 断言匹配路由
- 执行 Pre 过滤器链
- 转发到下游服务
- 执行 Post 过滤器链
- 返回响应
关键特性
- 高性能:基于 Netty 的非阻塞模型,优于传统 Servlet 网关。
- 动态路由:支持从注册中心(Nacos/Eureka)动态发现服务。
- 内置断言/过滤器工厂:Path、Method、Header、RateLimiter、Retry、CircuitBreaker 等。
- 易扩展:可自定义断言和过滤器。
与 Zuul 对比
- Zuul 1.x:同步阻塞模型,已进入维护状态。
- Spring Cloud Gateway:异步非阻塞,性能更高,官方推荐。
🔀 发散问题
- Q:过滤器链的执行顺序怎么定? → 按 order 洋葱模型,路由垫底,见本文档「Gateway 过滤器链的执行顺序是怎样的?」。
- Q:项目里选 Gateway 的理由怎么说? → 生态集成 + 异步高性能 + 扩展灵活,见本文档「你项目里为什么选择 Gateway 作为网关?」。
【中等】你项目里为什么选择 Gateway 作为网关?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:网关 / 选型理由
💎 关键结论
选 Gateway 五个理由:与 Spring Cloud 技术栈(Nacos/Sentinel/Security)无缝集成成本最低;WebFlux+Netty 异步非阻塞高并发线程开销小;内置断言过滤器工厂支撑灰度、限流、跨域等需求且易扩展;横切关注点(鉴权、日志、限流)统一收口;官方主推、版本迭代有保障。
⚡记忆卡片
- 口诀:生态合、性能高、能扩展、收横切、有人维
- 关键词:生态集成 / 异步非阻塞 / 灰度发布 / 横切关注点 / 官方主推
- 链路:技术栈匹配 → 性能达标 → 功能覆盖 → 维护风险低 → 选型成立
📖 核心知识
项目选择 Spring Cloud Gateway 的核心原因:
- 生态集成度高:项目基于 Spring Cloud 技术栈,Gateway 与 Nacos、Sentinel、Spring Security 等组件无缝配合,开发运维成本最低。
- 异步非阻塞高性能:基于 WebFlux + Netty 的响应式模型,处理高并发时线程开销小,优于 Zuul 1.x 的同步阻塞模型。
- 路由与过滤能力灵活:内置丰富的断言和过滤器工厂,支持灰度发布、限流熔断、跨域处理等复杂需求,且易于自定义扩展。
- 非功能性能力整合:可便捷集成限流熔断(如 Sentinel)、日志审计、统一鉴权等横切关注点。
- 社区活跃且持续演进:作为 Spring 官方主推网关,版本迭代有保障,长期维护风险低。
🔬 扩展知识
详情
- 【L3】回答选型题的加分点是说出"被否决的备选":Kong/APISIX 性能更高但二开需 Lua,团队没有相关能力;Zuul 1.x 已停维不考虑;最终 Gateway 是综合成本最低的选项。
- 【L4】Gateway 基于 WebFlux,要注意与传统 Spring MVC 应用的差异:不能使用 Servlet API(如 HttpServletRequest),阻塞操作会拖垮事件循环线程。
🔀 发散问题
- Q:Gateway 和 Dubbo 的定位区别? → 南北向入口 vs 东西向 RPC,见本文档「Dubbo 和 Spring Cloud Gateway 有什么区别?」。
- Q:灰度发布怎么用 Gateway 实现? → 动态路由把部分流量导向新版本,见本文档「Gateway 如何实现动态路由?」。
【中等】Dubbo 和 Spring Cloud Gateway 有什么区别?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:网关 / 架构定位
💎 关键结论
Dubbo 是 RPC 框架,解决服务之间(东西向流量)如何高性能调用;Gateway 是 API 网关,解决外部请求(南北向流量)如何进入微服务集群。二者是互补而非替代:Gateway 做统一入口,Dubbo 做内部高效调用。
⚡记忆卡片
- 口诀:Dubbo 管里面,Gateway 管门口
- 关键词:RPC / 东西向 / 南北向 / 服务治理 / 流量入口
- 链路:外部请求 → Gateway 路由鉴权 → 内部服务 → Dubbo RPC 互调
📖 核心知识
Dubbo 与 Spring Cloud Gateway 对比:
| 对比维度 | Dubbo | Spring Cloud Gateway |
|---|---|---|
| 核心定位 | RPC (远程过程调用) 框架 | API 网关 (流量入口) |
| 核心功能 | 服务间高性能调用、服务治理 | 请求路由、过滤链(安全、限流、日志) |
| 解决需求 | 服务之间如何调用 (东西向流量) | 外部请求如何进入微服务集群 (南北向流量) |
| 工作层次 | 服务层 (Service-to-Service) | 入口层 (Edge Service) |
| 关键能力 | 服务发现、负载均衡、容错、熔断 | 动态路由、身份认证、权限校验、限流 |
| 通信协议 | 默认 Dubbo 协议 (TCP)、HTTP、gRPC | HTTP、HTTPS (基于 WebFlux) |
总结与关系:在现代微服务架构中,二者是互补而非替代的关系。通常由 Spring Cloud Gateway 作为统一网关接收和处理所有外部请求,然后通过 Dubbo 在内部微服务之间进行高效、可靠的方法调用和治理。
🔬 扩展知识
详情
- 【L3】HTTP 与 RPC 的性能差异根源:HTTP 文本协议 + 短连接开销大,Dubbo 默认二进制协议 + TCP 长连接 + 连接复用,内部高频调用场景吞吐高得多,见 RPC 面试文档「HTTP 与 RPC 有什么区别?」。
- 【L4】Dubbo 3.x 推出 Triple 协议(基于 HTTP/2 + Protobuf,兼容 gRPC),模糊了 RPC 与 HTTP 的边界,网关甚至可以直接路由 Triple 流量。
🔀 发散问题
- Q:Feign(HTTP)和 Dubbo(RPC)怎么选? → 对外 REST 用 Feign,内部高频调用用 Dubbo,见本文档「Feign 和 Dubbo 有什么区别?」。
- Q:网关的南北向职责具体有哪些? → 路由、鉴权、限流、审计,见本文档「什么是 Spring Cloud Gateway?」。
【简单】什么是 Spring Cloud Zuul?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:网关 / Zuul
💎 关键结论
Zuul 是 Netflix 开源的基于 JVM 的 API 网关,提供动态路由、请求过滤、负载均衡(集成 Ribbon)、服务容错(集成 Hystrix)等能力,核心是可插拔的过滤器链架构(前置/路由/后置/错误四阶段)。1.x 同步阻塞模型性能瓶颈明显,已进入维护模式,新项目不推荐。
⚡记忆卡片
- 口诀:路由加过滤,Ribbon 分发,Hystrix 兜底
- 关键词:过滤器链 / 动态路由 / Ribbon / Hystrix / 停维
- 链路:请求 → 前置过滤器 → 路由 → 后置过滤器 → 响应(错误过滤器兜底)
📖 核心知识
Zuul 是 Netflix 开源的一款基于 JVM 的 API 网关,在微服务架构中充当系统对外的统一入口。它的核心作用是为后端服务提供路由和过滤功能。
其主要作用包括:
- 动态路由:作为所有外部请求的入口,根据配置的路由规则(如 URL 路径),将请求自动转发到后端的指定微服务。这实现了内部服务的调用细节对外部客户端的隐藏。
- 请求过滤:通过其强大的过滤器机制,在请求被路由前后执行统一的处理逻辑。这通常用于实现身份认证、安全校验、请求日志记录、性能监控等横切关注点。
- 负载均衡:集成 Ribbon,能够将请求负载均衡地分发到同一服务的多个实例上。
- 服务容错:集成 Hystrix,为路由提供熔断和降级保护,防止后端服务故障引发级联雪崩。
- 安全与监控:可在网关层统一进行访问控制和流量统计,收集关于请求和响应的数据,用于监控和分析。
Zuul 的核心是其可插拔的过滤器链架构。开发者通过实现自定义的过滤器,可以在请求生命周期的不同阶段(前置、路由、后置、错误)插入各种处理逻辑,从而使其功能具有很强的扩展性。
🔀 发散问题
- Q:Zuul 被什么取代了? → Spring Cloud Gateway,异步非阻塞且官方主推,见本文档「Spring Cloud 可以选择哪些 API 网关?」。
- Q:网关里的熔断现在用什么? → Hystrix 停更后由 Sentinel/Resilience4j 接管,见本文档「什么是 Hystrix?」。
【中等】你知道 Nacos 配置中心的实现原理吗?⭐⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:12 min | 🏷 标签:Nacos / 配置中心
💎 关键结论
Nacos 配置中心的核心机制是"客户端长轮询 + 服务端事件驱动 + 推拉结合":客户端携带配置 MD5 发起 30 秒长轮询,服务端配置变更时写库发事件、立即唤醒挂起请求返回变更,客户端再主动拉取新配置刷新 @RefreshScope;本地快照保证 Nacos 宕机时仍能用最后配置。
⚡记忆卡片
- 口诀:长轮询挂三十秒,一变即醒,推拉结合
- 关键词:长轮询 / MD5 比对 / 事件驱动 / 本地快照 / @RefreshScope
- 链路:配置变更 → 写库发事件 → 唤醒长轮询 → 客户端拉取 → 刷新 Bean
📖 核心知识
Nacos 配置中心的核心机制可以概括为:客户端长轮询 + 服务端事件驱动 + 推拉结合。
核心机制分解
- 客户端侧
- 长轮询:发送本地配置 MD5 值,请求挂起 30 秒,服务端有变更立即返回
- 本地快照:配置在本地存有快照文件,服务端不可用时作为容灾保障
- 监听触发:收到变更通知后主动拉取最新配置,发布事件刷新
@RefreshScope的 Bean
- 服务端侧
- 事件驱动:配置变更先写入数据库,再发布变更事件
- 唤醒长轮询:事件唤醒挂起的长轮询请求,立即通知客户端
- 集群同步:变更事件同步到集群其他节点,保持全局一致性
核心要点
- 推拉结合:长轮询实现准实时推送,客户端主动拉取具体变更内容
- MD5 比对:通过 MD5 值判断配置是否变化,避免无效传输
- 本地快照:保证 Nacos 宕机时应用仍能加载最后配置
🔬 扩展知识
详情
- 【L3】为什么用长轮询而不是真推送?真推送(服务端主动连接客户端)需要维护海量连接且网络环境复杂;长轮询用客户端发起的 HTTP 请求模拟推送,兼顾实时性(变更秒级送达)与简单性,30 秒超时后重连保证最终一致。
- 【L3】配置监听的生效范围:
@RefreshScope的 Bean 在刷新时会被销毁重建,而@ConfigurationProperties绑定的 Bean 天然支持刷新;静态字段/构造时固化的值不会被刷新,这是常见坑。 - 【L4】Nacos 配置存储默认用 Derby(单机)或外置 MySQL(集群),配置数据的集群一致性依赖共享数据库 + Distro 协议同步元数据。
🔀 发散问题
- Q:多环境配置怎么隔离? → 用 Namespace 划分 dev/test/prod,见本文档「Nacos 中的 Namespace 是什么?」。
- Q:基于 Git 的配置中心方案是什么? → Spring Cloud Config,需配合 Bus 广播刷新,见本文档「Spring Cloud Config 是什么?」。
【简单】Spring Cloud Config 是什么?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:配置中心 / Config
💎 关键结论
Spring Cloud Config 是分布式配置中心方案,分 Server 和 Client:Server 集中存配置(默认 Git 存储,天然版本控制)并暴露 HTTP 接口;Client 启动时按应用名 + 环境标识拉取配置加载到 Spring Environment。配置变更需配合 Bus 或 Actuator 手动刷新。
⚡记忆卡片
- 口诀:Git 存配置,Server 发接口,Client 拉环境
- 关键词:Config Server / Config Client / Git / Environment / Bus 刷新
- 链路:Git 仓库 → Config Server 暴露 HTTP → Client 按 name+profile 拉取 → 加载 Environment
📖 核心知识
Spring Cloud Config 是一套分布式配置中心解决方案,分为 Config Server 和 Config Client 两部分:
- Config Server 是一个集中式的配置服务器,所有微服务的配置文件都往这里存,底层默认用 Git 来管理配置内容,所以配置的版本控制天然就有了,谁改了什么、什么时候改的,一清二楚。
- Config Client 跑在每个微服务里,负责从 Config Server 拉取配置。服务启动时,根据自己的应用名和环境标识去请求对应的配置,拿到后直接加载到 Spring Environment 里用。
整体架构就是:
- Git 仓库存储各环境的配置文件
- Config Server 启动后连接 Git 仓库并暴露 HTTP 接口
- Config Client 启动时向 Config Server 发送请求,带上 application name 和 profile
- Config Server 从 Git 拉取对应的配置文件返回给 Client
- Client 将配置加载到 Spring Environment 中
🔀 发散问题
- Q:Config 改完配置客户端怎么感知? → 需配合 Spring Cloud Bus 广播或 Actuator refresh 端点,不像 Nacos 长轮询自动推送,见本文档「你知道 Nacos 配置中心的实现原理吗?」。
- Q:Config 和 Nacos 配置中心怎么选? → Nacos 自带实时推送 + 管理界面,新项目优先 Nacos,见本文档「SpringCloud 和 SpringCloud Alibaba 有什么区别?」。
【中等】Spring Cloud 支持哪些链路追踪方案?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:链路追踪 / 选型
💎 关键结论
四大主流方案:Sleuth+Zipkin(经典组合,生成 TraceId/SpanId + 收集展示)、Jaeger(CNCF 托管,适合大规模)、SkyWalking(Apache APM,Java Agent 零侵入、功能全)、OpenTelemetry(跨语言标准,未来主流)。注意 Spring Cloud 2022 版本后 Sleuth 项目不再推进,由 Micrometer Tracing 接棒。
⚡记忆卡片
- 口诀:Sleuth 埋点、Zipkin 展示、SW 零侵入、OTel 定标准
- 关键词:TraceId / SpanId / Java Agent / OpenTelemetry / Micrometer Tracing
- 链路:入口生成 TraceId → 跨服务透传 → 采集器上报 → 存储展示
📖 核心知识
Spring Cloud 生态里常用的链路追踪方案有这么几个:
- Spring Cloud Sleuth + Zipkin,这是 Spring Cloud 生态最经典的组合。Sleuth 负责在每个请求里自动生成 TraceId 和 SpanId,Zipkin 负责收集和展示追踪数据。
- Jaeger,CNCF 托管的分布式追踪系统,比 Zipkin 更适合大规模系统,支持几十万级别的 span 存储和查询。Spring Cloud Sleuth 也能和 Jaeger 对接。
- SkyWalking,Apache 开源的 APM 平台,不光能做链路追踪,还支持指标监控、告警、服务依赖分析,功能比较全。它用 Java Agent 方式接入,代码零侵入。
- OpenTelemetry,由 OpenTracing 和 OpenCensus 合并而来,是现在云原生应用推荐的追踪标准,跨平台跨语言支持好,是未来的主流方向。
🔬 扩展知识
详情
- 【L3】Sleuth 的演进:Spring Cloud 2022.0 版本起 Sleuth 项目不再推进,链路追踪能力由 Micrometer Tracing 承担(配合 Brave 或 OpenTelemetry 桥接器上报 Zipkin/Jaeger/OTLP),新项目直接用 Micrometer Tracing + OpenTelemetry。
- 【L3】接入方式三大流派:SDK 侵入式(Sleuth)、Agent 字节码增强(SkyWalking)、无侵入 Sidecar/OTel Collector,侵入性越低接入成本越低但定制能力越弱。
- 【L4】采样是生产必备:全量采集在大流量下存储成本极高,一般按概率(10%)或限速(100 条/秒)采样,错误链路则强制全量采集。
🔀 发散问题
- Q:Sleuth+Zipkin 内部怎么传递上下文? → 靠 HTTP Header(X-B3-TraceId 等),见本文档「Sleuth + Zipkin 的工作原理是什么?」。
- Q:SkyWalking 和 Zipkin 怎么选? → 新项目推荐 SkyWalking(零侵入 + 功能全),见本文档「SkyWalking 和 Zipkin 有什么区别?」。
分布式通信
【简单】什么是 Feign?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:Feign / 服务调用
💎 关键结论
Feign 是声明式 HTTP 客户端:定义接口 + 注解描述请求,运行时动态生成实现发 HTTP 调用。核心价值是把远程调用封装成本地接口:集成 Ribbon/LoadBalancer 负载均衡、复用 Spring MVC 注解、可插拔编解码。注意 @RequestParam 必须指定 name、复杂参数默认 POST JSON。
⚡记忆卡片
- 口诀:接口声明,代理实现,像调本地方法
- 关键词:@FeignClient / 动态代理 / Ribbon / RequestTemplate / 可插拔编解码
- 链路:扫描 @FeignClient → 生成动态代理 → 注解构建请求 → 负载均衡选实例 → HTTP 调用 → 反序列化
📖 核心知识
Feign 是一个声明式的 HTTP 客户端,旨在简化微服务之间的远程调用。开发者只需定义 Java 接口并通过注解描述请求细节,Feign 在运行时动态生成实现,完成实际的 HTTP 请求。
核心特性
- 声明式编程:使用
@FeignClient注解声明客户端,接口方法映射为 HTTP 请求,大幅降低代码量。 - 负载均衡:默认集成 Ribbon,通过服务名自动发现实例并轮询调用。
- 与 Spring MVC 兼容:复用
@RequestMapping、@PathVariable等注解,学习成本低。 - 可插拔编解码:支持 Jackson、Gson 等消息转换器,灵活处理请求/响应体。
工作原理
- 启动时扫描
@FeignClient接口,为其生成动态代理。 - 调用接口方法时,代理根据注解构建请求 URL、参数、头信息。
- 通过 Ribbon 选择目标服务实例,执行 HTTP 调用。
- 将响应反序列化为方法返回类型。
关键注意点
- 参数注解:
@RequestParam必须指定 name,否则可能失败。 - 复杂参数:默认以 POST 发送 JSON,服务端需使用
@RequestBody接收。 - 超时配置:需在配置文件中设置
ribbon.ReadTimeout和ribbon.ConnectTimeout。 - 熔断降级:早期集成 Hystrix,Spring Cloud 2020.0.0+ 推荐使用 Resilience4j。
Feign 的核心价值在于将远程调用封装成本地接口,让微服务间通信变得直观、高效。
🔀 发散问题
- Q:Feign 和 OpenFeign 什么关系? → OpenFeign 是 Spring 增强版,支持 Spring MVC 注解,见本文档「Feign 和 OpenFeign 有什么区别?」。
- Q:Feign 的负载均衡具体怎么实现? → 动态代理拦截 + 注册中心拉实例列表 + 负载均衡器选实例,见本文档「Feign 是如何实现负载均衡的?」。
【中等】Feign 是如何实现负载均衡的?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:Feign / 负载均衡
💎 关键结论
Feign 本身不实现负载均衡,而是集成 Ribbon(新版用 Spring Cloud LoadBalancer)做客户端负载均衡:动态代理拦截方法调用 → 按服务名从注册中心拉实例列表 → 负载均衡器按策略(轮询/随机/响应时间加权)选一个实例 → 替换 URL 为 IP:Port 发起真实 HTTP 请求。
⚡记忆卡片
- 口诀:Feign 拦截,注册中心给列表,均衡器挑实例
- 关键词:客户端负载均衡 / ILoadBalancer / Spring Cloud LoadBalancer / 轮询/随机
- 链路:代理拦截 → 服务名查实例列表 → 策略选实例 → URL 替换 → HTTP 调用
📖 核心知识
Feign 本身是一个声明式 HTTP 客户端,不直接实现负载均衡,而是通过集成 Ribbon(或较新版本中的 Spring Cloud LoadBalancer)来完成客户端负载均衡。
核心实现原理
- 动态代理拦截:Feign 在运行时为接口生成动态代理,每次方法调用被拦截后,根据
@FeignClient注解中的服务名(name或value)构造请求 URL。 - 服务发现获取列表:Feign 会从服务注册中心(如 Nacos、Eureka)拉取该服务名对应的所有可用实例列表。
- 负载均衡器选择实例:Feign 将服务名和实例列表交给内置的负载均衡器(如 Ribbon 的
ILoadBalancer),由负载均衡器根据配置的策略(轮询、随机、响应时间加权等)选出一个目标实例。 - 发起真实请求:Feign 将请求 URL 替换为所选实例的 IP 和端口,通过 HTTP 客户端(如 OkHttp、Apache HttpClient)发起调用。
底层组件替换
- 早期版本默认集成 Ribbon(需引入
spring-cloud-starter-netflix-ribbon)。 - 新版本官方推荐使用 Spring Cloud LoadBalancer,它是与 Spring Cloud Commons 集成的反应式负载均衡器,无需额外依赖即可与 Feign 配合使用。
配置负载均衡策略(Ribbon 示例)
# 为指定服务配置负载均衡策略(Ribbon 示例)
service-name:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule🔬 扩展知识
详情
- 【L3】客户端负载均衡 vs 服务端负载均衡:Nginx 是集中式服务端均衡,配置集中但多一跳;Ribbon/LoadBalancer 在调用方进程内选实例,无额外中间层但每个客户端都要维护实例列表与策略。
- 【L3】Spring Cloud LoadBalancer 默认只有轮询和随机两种策略,需要一致性哈希等策略时要自定义
ReactorServiceInstanceLoadBalancer。 - 【L4】Ribbon 已进入维护模式(Netflix 停更),Spring Cloud 2020.0 起默认移除 Ribbon 依赖,新项目一律用 Spring Cloud LoadBalancer。
🔀 发散问题
- Q:第一次调用为什么特别慢? → 懒加载初始化开销,饥饿加载可解,见本文档「为什么 Feign 第一次调用耗时很长?」。
- Q:实例列表从哪里来? → 注册中心拉取 + 本地缓存,见本文档「Spring Cloud 如何实现服务注册?」。
【中等】为什么 Feign 第一次调用耗时很长?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:Feign / 性能优化
💎 关键结论
Feign 首次调用慢是懒加载设计的初始化开销:服务发现、负载均衡器、HTTP 客户端连接池都在首次调用时才初始化,集成 Hystrix 时还要建线程池/信号量,首次可能叠加 DNS 解析与注册中心拉取。解法:ribbon.eager-load.enabled=true 饥饿加载,或启动后预热一次调用。
⚡记忆卡片
- 口诀:首次慢是懒加载,饥饿加载治根
- 关键词:懒加载 / 连接池初始化 / eager-load / 预热
- 链路:首次调用 → 初始化 LB/连接池/线程池 → 叠加 DNS/注册中心拉取 → 耗时尖刺
📖 核心知识
Feign 首次调用慢是懒加载设计的初始化开销,通过饥饿加载或预热可解决。
核心原因:
- 懒加载机制:服务发现、负载均衡器、HTTP 客户端(连接池)均在首次调用时初始化,而非启动时。
- 额外组件初始化:若集成 Hystrix,首次调用需创建线程池或信号量。
- 网络开销:首次调用可能触发 DNS 解析或从注册中心拉取服务列表。
解决方案:
- 启用饥饿加载:配置
ribbon.eager-load.enabled=true,让负载均衡器启动时预初始化。 - 预热调用:应用启动后主动发起一次健康检查或空请求,触发初始化完成。
🔬 扩展知识
详情
- 【L3】这个问题的本质与连接池预热同源:HttpClient/OkHttp 首次请求要完成 TCP 握手(若 HTTPS 还要 TLS 握手),生产上常配合连接池预热 + 就绪探针(readiness probe)延迟接流,避免发布瞬间 RT 尖刺。
- 【L3】Spring Cloud LoadBalancer 体系下对应配置为
spring.cloud.loadbalancer.eager-load.clients,指定需要提前初始化的服务名列表。
🔀 发散问题
- Q:Feign 超时怎么配? → feign.client.config 分级配置 connect/read timeout,见本文档「OpenFeign 如何配置超时和重试?」。
- Q:负载均衡器初始化了什么? → 实例列表缓存 + 策略对象,见本文档「Feign 是如何实现负载均衡的?」。
【中等】Feign 和 OpenFeign 有什么区别?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:Feign / 组件对比
💎 关键结论
OpenFeign 是 Feign 的 Spring Cloud 官方增强版,是当前的事实标准:归属上 Feign 是 Netflix 组件、OpenFeign 是 Spring 封装;注解上 OpenFeign 额外支持 Spring MVC 注解;集成上深度对接 Spring 生态(自动配置、消息转换器、负载均衡器);依赖上对应 spring-cloud-starter-openfeign。
⚡记忆卡片
- 口诀:Feign 裸奔,OpenFeign 穿 Spring 外套
- 关键词:Netflix / Spring 封装 / Spring MVC 注解 / starter-openfeign
- 链路:Feign(原生)→ Spring 封装增强 → OpenFeign → 支持 MVC 注解 + 自动配置
📖 核心知识
OpenFeign 是 Feign 的 Spring 增强版,是当前 Spring Cloud 项目的事实标准。
- 归属:Feign 是 Netflix 组件;OpenFeign 是 Spring Cloud 官方封装版。
- 注解:Feign 仅支持自身注解;OpenFeign 额外支持 Spring MVC 注解(如
@RequestMapping),开发更便捷。 - 集成:OpenFeign 深度集成 Spring 生态(自动配置、消息转换器、负载均衡器)。
- 依赖:OpenFeign 对应
spring-cloud-starter-openfeign(新版),Feign 对应旧版 starter。
🔬 扩展知识
详情
- 【L3】OpenFeign 的关键增强在
SpringMvcContract:把 Spring MVC 注解(@GetMapping、@PathVariable 等)翻译成 Feign 的 MethodMetadata,这是"复用 MVC 注解"能力的实现根源。 - 【L4】OpenFeign 底层客户端默认是 JDK HttpURLConnection(无连接池),生产建议替换为 OkHttp 或 Apache HttpClient 并开启连接池,显著提升高并发吞吐。
🔀 发散问题
- Q:OpenFeign 内部完整工作流程? → 动态代理 + Contract 解析 + 负载均衡 + Client 发送,见本文档「OpenFeign 的工作原理是什么?」。
- Q:OpenFeign 首次调用慢怎么破? → 饥饿加载/预热,见本文档「为什么 Feign 第一次调用耗时很长?」。
【简单】Feign 和 Dubbo 有什么区别?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:8 min | 🏷 标签:服务调用 / 选型对比
💎 关键结论
Feign 是 HTTP 风格客户端,重 Spring 生态集成;Dubbo 是高性能 RPC 框架,重服务治理与性能。四大差异:协议(HTTP/JSON vs TCP 二进制)、负载均衡(Feign 靠集成 Ribbon/LoadBalancer,Dubbo 内置多策略)、治理能力(Dubbo 内置路由/容错/降级,Feign 靠生态拼装)、生态(Feign 无缝 Spring Cloud,Dubbo 可经 Alibaba 整合)。
⚡记忆卡片
- 口诀:Feign 走 HTTP 靠生态,Dubbo 走 TCP 拼性能
- 关键词:HTTP / RPC / 内置治理 / 一致性哈希 / Spring Cloud Alibaba
- 链路:对外 REST → Feign;内部高频 → Dubbo
📖 核心知识
Feign 是 HTTP 风格的客户端,注重 Spring 生态集成;Dubbo 是高性能 RPC 框架,注重服务治理和性能。
- 通信协议:Feign 基于 HTTP 协议,通常用于 RESTful API 调用;Dubbo 基于自定义 TCP 协议(如 Netty),性能更高,适合内部服务间高频调用。
- 负载均衡:Feign 本身不具备负载均衡能力,需集成 Ribbon 或 Spring Cloud LoadBalancer;Dubbo 内置多种负载均衡策略(随机、轮询、一致性哈希等)。
- 服务治理:Dubbo 提供丰富的服务治理功能(服务路由、容错、降级、限流等),Feign 依赖 Spring Cloud 生态(如 Hystrix、Sentinel)组合实现。
- 生态集成:Feign 与 Spring Cloud 生态无缝融合,特别适合 Spring Boot 项目;Dubbo 是独立 RPC 框架,可通过 Spring Cloud Alibaba 整合到 Spring Cloud 体系。
🔀 发散问题
- Q:HTTP 和 RPC 的性能差异根源? → 文本 vs 二进制、短连接 vs 长连接,见 RPC 面试文档「HTTP 与 RPC 有什么区别?」。
- Q:网关和 Dubbo 的分工? → 南北向入口 vs 东西向调用,见本文档「Dubbo 和 Spring Cloud Gateway 有什么区别?」。
微服务架构
【中等】单体架构和微服务架构有什么区别?⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:架构 / 架构对比
💎 关键结论
单体把所有功能打包在一个应用,部署简单、本地事务、运维成本低,但扩展只能整体扩、局部故障可能拖垮全局;微服务按业务边界拆分独立部署,可独立扩展、故障隔离、团队并行,但引入远程调用、分布式事务、高运维成本。业务复杂、团队大才值得拆。
⚡记忆卡片
- 口诀:单体简单拖不动,微服务灵活管不起
- 关键词:独立部署 / 独立数据库 / 故障隔离 / 分布式事务 / 运维成本
- 链路:业务复杂度 + 团队规模 + 扩展需求 → 评估拆分收益 vs 分布式成本 → 决策
📖 核心知识
| 维度 | 单体架构 | 微服务架构 |
|---|---|---|
| 架构形态 | 所有功能打包在一个应用中 | 按业务边界拆分为多个独立服务 |
| 部署方式 | 一个 WAR/JAR 包部署 | 每个服务独立部署 |
| 技术栈 | 统一技术栈 | 每个服务可选不同技术栈 |
| 数据库 | 共享单一数据库 | 每个服务独立数据库(理想情况) |
| 通信方式 | 进程内方法调用 | HTTP/RPC 远程调用 |
| 扩展性 | 整体扩展(即使只有某模块高负载) | 按需对单个服务扩展 |
| 故障影响 | 局部故障可能导致整体崩溃 | 故障隔离在单个服务内 |
| 开发效率 | 小项目快,大项目协作困难 | 团队独立开发,并行迭代 |
| 运维成本 | 低(一套应用) | 高(多套服务,需自动化运维) |
| 数据一致性 | 本地事务即可 | 需分布式事务方案 |
微服务适用场景:业务复杂度高、团队规模大、需独立扩展某些模块、对故障隔离有要求。
微服务不适用场景:业务简单、团队小、对延迟极其敏感(跨服务调用增加网络开销)。
🔬 扩展知识
详情
- 【L3】微服务的隐性成本清单:服务发现/配置中心/网关等基础设施、分布式事务、链路追踪、多服务发布与回滚、数据一致性对账——拆分前先评估团队能否负担这些成本。
- 【L4】模块化单体(Modular Monolith)是中间路线:进程内按模块隔离边界,保留部署简单性,需要时再按模块缝拆出微服务。
🔀 发散问题
- Q:决定拆了之后怎么拆? → 按 DDD 限界上下文 + 单一职责,见本文档「微服务如何拆分?」。
- Q:跨服务数据一致性怎么保? → Seata 分布式事务,见本文档「什么是 Seata?」。
【中等】微服务如何拆分?⭐
🎯 目标等级:L2 | ⏱ 建议用时:12 min | 🏷 标签:架构 / 服务拆分
💎 关键结论
拆分五原则:单一职责、DDD 限界上下文、服务自治(独立数据库)、粒度适中、高内聚低耦合。四种策略:按业务能力、按子域、按变更频率、按团队规模。三大红线:避免分布式单体、跨服务事务用 Seata/最终一致、接口版本化 + 渐进式拆分。
⚡记忆卡片
- 口诀:DDD 划边界,一服务一库,渐进拆不一步到位
- 关键词:限界上下文 / 服务自治 / 分布式单体 / API 版本化 / 渐进式
- 链路:梳理业务域 → DDD 划上下文 → 评估粒度与团队 → 渐进拆分
📖 核心知识
微服务拆分是架构设计的核心难点,常用原则和策略如下:
拆分原则
- 单一职责原则(SRP):每个服务只负责一个业务领域,如订单服务只管订单。
- 领域驱动设计(DDD):按业务领域边界拆分,每个限界上下文对应一个微服务。
- 服务自治:每个服务拥有独立的数据存储,不与其他服务共享数据库。
- 合适的粒度:不宜过细(导致远程调用开销大、运维复杂)也不宜过粗(失去拆分意义)。
- 高内聚低耦合:服务内部功能高度相关,服务间依赖尽量少。
拆分策略
| 策略 | 说明 | 示例 |
|---|---|---|
| 按业务能力拆分 | 根据业务功能模块划分 | 用户服务、订单服务、商品服务 |
| 按子域拆分 | 基于 DDD 限界上下文 | 电商系统中分为商品域、交易域、营销域 |
| 按变更频率拆分 | 将频繁变化的部分独立 | 营销活动服务独立于核心交易 |
| 按团队规模拆分 | 每个服务可由一个小团队(5-8 人)负责 | 避免跨团队协作瓶颈 |
拆分注意事项
- 避免分布式单体:拆分后服务间仍强耦合(如同步调用链路过长),还不如单体。
- 数据一致性:跨服务事务需引入 Seata 等分布式事务方案,或采用最终一致性。
- API 版本管理:服务间接口需版本化,避免升级时互相影响。
- 逐步拆分:从单体到微服务应渐进式拆分,而非一次性重构。
🔬 扩展知识
详情
- 【L3】拆分前的量化信号:单次发布需协调多个团队、构建时间超长、不同模块扩缩容需求差异大——这些比"服务数量"更能说明到了该拆的时候。
- 【L4】绞杀者模式(Strangler Fig):新需求写成新服务,老功能逐步代理到新服务,避免一次性重构的交付风险。
🔀 发散问题
- Q:拆分后雪崩风险怎么防? → 超时 + 熔断 + 隔离全链路防护,见本文档「什么是服务雪崩?如何做全链路防护?」。
- Q:跨服务事务具体怎么做? → Seata AT/TCC/Saga/XA 四模式,见本文档「Seata 支持哪些模式的分布式事务?」。
【中等】SpringCloud 和 SpringCloud Alibaba 有什么区别?⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:SpringCloud / 生态对比
💎 关键结论
核心差异在组件维护状态:Netflix 套件大部分已停更(Hystrix、Zuul、Eureka),Alibaba 套件活跃维护(Nacos、Sentinel、Seata、RocketMQ)。新项目首选 Alibaba 全家桶:Nacos(注册+配置)+ Sentinel(熔断限流)+ Gateway(共用)+ OpenFeign + Seata。
⚡记忆卡片
- 口诀:Netflix 停更,Alibaba 接棒,Nacos 一身双职
- 关键词:停更 / Nacos / Sentinel / Seata / RocketMQ
- 链路:注册 Eureka→Nacos;熔断 Hystrix→Sentinel;网关 Zuul→Gateway;事务无→Seata
📖 核心知识
| 维度 | SpringCloud (Netflix) | SpringCloud Alibaba |
|---|---|---|
| 维护状态 | 大部分组件已停更(Hystrix、Zuul、Eureka) | 活跃维护中 |
| 注册中心 | Eureka(AP) | Nacos(AP/CP 可切换) |
| 配置中心 | Spring Cloud Config(基于 Git) | Nacos Config(自带存储) |
| 熔断降级 | Hystrix(停更) | Sentinel |
| 网关 | Zuul(停更) | Spring Cloud Gateway(共用) |
| 服务调用 | Feign/OpenFeign | OpenFeign / Dubbo |
| 分布式事务 | 无官方方案 | Seata |
| 消息队列 | 无 | RocketMQ |
| 社区 | 国际社区为主 | 国内活跃,阿里背书 |
选型建议:新项目首选 SpringCloud Alibaba 全家桶(Nacos + Sentinel + Gateway + OpenFeign + Seata),组件活跃度高、功能丰富、国内生态好。
🔬 扩展知识
详情
- 【L3】两者不是对立关系:SpringCloud Alibaba 本质是 Spring Cloud 的实现集合(遵守 Spring Cloud 标准 SPI),可以和原生组件混用,如 Nacos 注册 + Spring Cloud Gateway + Config。
- 【L4】版本对应是关键坑点:Spring Cloud Alibaba 版本需同时匹配 Spring Cloud 与 Spring Boot 版本(如 2021.x 对应 Cloud 2021.0.x + Boot 2.6.x),升级前必查官方版本兼容矩阵。
🔀 发散问题
- Q:Spring Cloud 整体有哪些组件分类? → 九大类组件总览,见本文档「Spring Cloud 有哪些核心组件?」。
- Q:注册中心具体怎么选? → Nacos/Consul/Eureka/ZK 对比,见本文档「Spring Cloud 有哪些注册中心?」。
注册中心深入
【中等】Eureka 的自我保护机制是什么?⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:注册中心 / Eureka
💎 关键结论
自我保护是应对网络分区的容错策略:15 分钟内心跳失败比例超 85% 时触发,Server 停止剔除任何实例(即使已过期),宁可保留可能失效的注册信息也不盲目删除。本质是 AP 优先:牺牲注册表准确性换可用性。生产建议保持开启。
⚡记忆卡片
- 口诀:心跳丢八成五,冻结剔除保可用
- 关键词:85% / 15 分钟 / 停止剔除 / AP 优先 / 网络分区
- 链路:网络分区 → 心跳大面积丢失 → 触发自我保护 → 注册表冻结 → 分区恢复后自愈
📖 核心知识
Eureka 的自我保护机制是一种应对网络分区故障的容错策略,核心思想是"宁可保留错误的服务注册信息,也不盲目删除可能正常的服务"。
触发条件
当 Eureka Server 在 15 分钟内心跳失败比例超过 85%(即 85% 的客户端未按时续约),会触发自我保护机制。
触发后的行为
- 停止剔除:Server 不再从注册表中删除任何服务实例,即使它们已过期。
- 保留注册表:维护当前注册表数据不变,防止网络分区恢复后大量服务被误删。
设计权衡
| 场景 | 不开启自我保护 | 开启自我保护 |
|---|---|---|
| 网络分区 | 大量服务被误删,调用方找不到服务 | 服务列表保留,调用方可能调用到不可达实例 |
| 服务真实下线 | 正常剔除 | 无法剔除,需等待网络恢复 |
| 适用场景 | 对服务列表准确性要求高 | 对服务可用性要求高(AP 优先) |
配置方式
eureka:
server:
enable-self-preservation: true # 开启自我保护(默认 true)
eviction-interval-timer-in-ms: 60000 # 剔除过期实例的间隔(默认 60s)生产环境建议保持开启。若关闭,网络抖动可能导致大面积服务被误剔除,引发雪崩。
🔬 扩展知识
详情
- 【L3】自我保护的代价:真实下线的实例也不会被剔除,调用方可能拿到不可达地址,需靠客户端重试 + 快速失败兜底;这就是 AP 模型的典型取舍。
- 【L3】开发环境常关闭自我保护,否则频繁重启服务会让注册表堆积大量过期实例,干扰调试。
- 【L4】Nacos 的对应机制叫"推空保护"(服务列表为空时不推送),思路一致:宁可给旧数据,不给空数据。
🔀 发散问题
- Q:剔除机制的常规流程是什么? → 90 秒无心跳标记过期,60 秒扫描清理,见本文档「Eureka 的实现原理说一下?」。
- Q:AP 和 CP 在注册中心的体现? → Nacos 双模式可切换,见本文档「Nacos 的 AP 和 CP 模式如何切换?」。
【中等】Nacos 的 AP 和 CP 模式如何切换?⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:Nacos / CAP
💎 关键结论
Nacos 默认 AP(Distro 协议),支持临时实例与持久实例自动区分:临时实例走 Distro(AP)用于服务发现,持久实例走 Raft(CP);配置中心内部用 CP 保证变更一致。可通过 API 临时切换模式(重启失效),但 1.3.0+ 后按实例类型自动选择,一般无需手动切。
⚡记忆卡片
- 口诀:临时走 AP,持久走 CP,配置用 Raft
- 关键词:Distro / Raft / 临时实例 / 持久实例 / 模式切换 API
- 链路:服务发现 → 临时实例 → AP 保可用;配置/持久实例 → CP 保一致
📖 核心知识
Nacos 同时支持 AP 和 CP 两种一致性模型,默认使用 AP 模式。
两种模式对比
| 维度 | AP 模式(默认) | CP 模式 |
|---|---|---|
| 一致性 | 最终一致性 | 强一致性 |
| 可用性 | 高(分区时仍可读写) | 分区时少数派不可用 |
| 实现 | Distro 协议(每个节点独立处理) | Raft 协议(Leader 写入) |
| 数据持久化 | 内存 + 磁盘快照 | 内存 + Raft 日志 |
| 适用场景 | 服务发现(临时实例) | 配置管理、持久实例 |
切换方式
# 通过 API 临时切换(重启后失效)
curl -X PUT 'http://127.0.0.1:8848/nacos/v1/ns/operator/modes?mode=CP'🔬 扩展知识
详情
- 【L3】实际应用约定:服务注册发现用 AP 模式(默认),服务实例是临时的,优先保证可用性;配置管理内部使用 CP 模式(Raft),保证配置变更的一致性。
- 【L3】Nacos 1.3.0+ 以后,临时实例用 Distro(AP),持久实例用 Raft/JRaft(CP),按实例类型自动选择,无需手动切换。
- 【L4】注意区分两个概念:注册中心模式切换(上节 API)影响的是服务实例的一致性语义;而持久实例(
ephemeral=false)常用于 K8s 之外的 DNS-F 场景或需持久化服务清单的场景。
🔀 发散问题
- Q:注册中心选 AP 还是 CP? → 服务发现优先 AP(可用性),见本文档「Eureka 的自我保护机制是什么?」。
- Q:Nacos 还有哪些隔离维度? → Namespace/Group/DataId 三元组,见本文档「Nacos 中的 Namespace 是什么?」。
服务调用深入
【困难】OpenFeign 的工作原理是什么?⭐⭐
🎯 目标等级:L3 | ⏱ 建议用时:15 min | 🏷 标签:OpenFeign / 工作原理
💎 关键结论
OpenFeign 核心原理是动态代理 + 注解解析 + HTTP 调用:启动时扫描 @FeignClient 接口生成 JDK 动态代理并注册为 Bean;调用时代理拦截,Contract 解析 Spring MVC 注解构建 RequestTemplate,经 LoadBalancerClient 把服务名解析为 IP:Port,由 Client(默认 HttpURLConnection,可换 OkHttp)发请求,Decoder 反序列化响应。
⚡记忆卡片
- 口诀:扫描建代理,契约解注解,均衡定实例,Client 发请求
- 关键词:@EnableFeignClients / Contract / RequestTemplate / LoadBalancerClient / Decoder
- 链路:扫描注册 → 生成代理 → 方法拦截 → 解析注解 → 负载均衡 → 执行请求 → 解码响应
📖 核心知识
OpenFeign 是声明式 HTTP 客户端,核心原理是动态代理 + 注解解析 + HTTP 调用。
工作流程
- 扫描注册:Spring 启动时扫描
@EnableFeignClients指定的包,找到所有@FeignClient接口。 - 生成代理:为每个接口创建 JDK 动态代理对象,注册为 Bean。
- 方法拦截:调用接口方法时,代理对象拦截调用,进入
FeignInvocationHandler。 - 解析注解:
Contract组件解析方法上的 Spring MVC 注解(@RequestMapping、@PathVariable等),构建RequestTemplate。 - 负载均衡:将服务名通过
LoadBalancerClient解析为具体 IP:Port。 - 执行请求:由
Client(默认HttpURLConnection,可替换为 OkHttp/Apache HttpClient)发送 HTTP 请求。 - 解码响应:
Decoder将响应体反序列化为方法返回类型。
核心组件
| 组件 | 职责 |
|---|---|
Contract | 解析接口注解,生成 MethodMetadata |
Encoder | 序列化请求体 |
Decoder | 反序列化响应体 |
Client | 执行 HTTP 请求 |
Interceptor | 请求拦截器,添加 Header 等 |
Retryer | 重试策略 |
Logger | 日志记录 |
自定义拦截器示例:透传登录 Token
@Component
public class AuthRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 从上下文获取 token 并添加到请求头
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader("Authorization");
if (StringUtils.isNotBlank(token)) {
template.header("Authorization", token);
}
}
}🔬 扩展知识
详情
- 【L3】Bean 注册机制:
FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar,启动时把每个@FeignClient接口注册为FeignClientFactoryBean,注入时由工厂 Bean 创建代理对象——这就是"接口能直接 @Autowired"的原因。 - 【L3】
LoadBalancerClient是抽象:Ribbon 时代由RibbonLoadBalancerClient实现,现在由 Spring Cloud LoadBalancer 的BlockingLoadBalancerClient/ReactiveLoadBalancer实现,Feign 本身不感知具体实现。 - 【L4】XID/TraceId 透传都靠 RequestInterceptor 把上下文写入 Header,下游从 Header 还原——分布式事务与链路追踪在调用层的落点都是这里。
- 【L4】默认 Client 是
HttpURLConnection,无连接池,高并发场景应替换为 OkHttp/Apache HttpClient 并配置连接池,否则每请求新建连接,吞吐受限且 TIME_WAIT 堆积。
🏭 实战场景
详情
生产中最常见的 OpenFeign 配置缺陷是未配超时:feign.Request.Options 默认 read timeout 60 秒,下游假死时上游线程被长时间占用引发雪崩。标准做法:全局默认 connect 5 秒/read 10 秒,核心接口按 P99 × 2 单独收紧,并配合 Sentinel 熔断。另一高频需求是登录态透传:网关鉴权后把用户信息放入 Header,用 RequestInterceptor 在服务间逐级传递,避免重复登录。
⚠️ 常见误区
详情
常见误区:
- ❌ “Feign 接口是 HTTP 客户端,和 Spring 容器无关” → 每个
@FeignClient都被注册为 Spring Bean(FeignClientFactoryBean),依赖注入、拦截器、配置都走容器。 - ❌ “@RequestParam 不写 name 也行” → Feign 的注解解析要求显式 name,省略可能导致参数丢失或 400。
- ❌ “配置了超时就够了” → 超时要和重试、熔断联动:上游超时 > 下游超时 × 重试次数,否则重试形同虚设。
- ❌ “复杂对象参数直接传就行” → 默认以 POST + JSON 发送,服务端必须用
@RequestBody接收,否则解析失败。
🔀 发散问题
- Q:超时和重试具体怎么配? → feign.client.config 分级配置 + Retryer Bean,见本文档「OpenFeign 如何配置超时和重试?」。
- Q:负载均衡那一步是怎么选实例的? → 注册中心拉列表 + 策略选择,见本文档「Feign 是如何实现负载均衡的?」。
- Q:首次调用慢和这里哪个环节有关? → LoadBalancer/Client 懒加载初始化,见本文档「为什么 Feign 第一次调用耗时很长?」。
【中等】OpenFeign 如何配置超时和重试?⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:OpenFeign / 超时重试
💎 关键结论
超时用 feign.client.config 分级配置:default 全局 + 按服务名单独覆盖 connect-timeout/read-timeout;重试用 Retryer Bean(默认不重试,Retryer.Default(100,1000,3) 指初始间隔/最大间隔/最大次数)。三条铁律:仅幂等接口重试、上游超时 > 下游超时 + 重试时间、重试配合熔断防放大。
⚡记忆卡片
- 口诀:超时分级配,重试幂等行,预算要盖住
- 关键词:feign.client.config / connect-timeout / Retryer / 幂等 / 超时级联
- 链路:全局超时 → 服务级覆盖 → Retryer 控制次数 → 与熔断联动
📖 核心知识
超时配置
# 全局配置
feign:
client:
config:
default: # default 表示全局
connect-timeout: 5000 # 连接超时 5s
read-timeout: 10000 # 读取超时 10s
user-service: # 针对特定服务
connect-timeout: 3000
read-timeout: 5000
# OpenFeign + LoadBalancer 配置(Spring Cloud 2020+)
spring:
cloud:
openfeign:
client:
config:
default:
connect-timeout: 5000
read-timeout: 10000重试配置
@Configuration
public class FeignConfig {
@Bean
public Retryer feignRetryer() {
// 参数:初始间隔、最大间隔、最大重试次数
return new Retryer.Default(100, 1000, 3);
}
}🔬 扩展知识
详情
- 【L3】重试与幂等性:只有幂等接口(GET、PUT)才适合开启重试,非幂等接口(POST)重试可能导致重复创建。
- 【L3】超时级联:上游服务的超时应大于下游服务超时 + 重试时间,避免上游先超时导致级联失败;经验值是上游超时 > 下游超时 × 重试次数。
- 【L3】熔断配合:重试次数过多可能加剧下游压力,应配合熔断器使用;Retryer 的重试发生在客户端内部,熔断器统计的失败次数会包含重试后的最终结果。
- 【L4】注意 Spring Cloud 2020+ 的配置前缀从
feign.client迁移到spring.cloud.openfeign.client,升级时容易遗漏导致配置静默失效。
🔀 发散问题
- Q:不配超时会怎样? → 默认 60 秒,下游假死拖垮上游线程池,见本文档「什么是服务雪崩?如何做全链路防护?」。
- Q:Retryer 在调用链的哪个环节生效? → Client 发送失败后、解码前,见本文档「OpenFeign 的工作原理是什么?」。
网关深入
【困难】Spring Cloud Gateway 的工作原理是什么?⭐⭐
🎯 目标等级:L3 | ⏱ 建议用时:15 min | 🏷 标签:网关 / 工作原理
💎 关键结论
Gateway 基于 WebFlux + Netty,架构三件套:Route、Predicate、Filter。请求由 Netty 接收,RoutePredicateHandlerMapping 遍历路由用断言匹配,命中后 FilteringWebHandler 执行过滤器链(Pre 阶段鉴权限流 → 转发下游 → Post 阶段包装响应),全程响应式非阻塞。
⚡记忆卡片
- 口诀:断言选路由,过滤器加工,Netty 转发
- 关键词:RoutePredicateHandlerMapping / FilteringWebHandler / Pre/Post / lb:// / 断言工厂
- 链路:Netty 接收 → 断言匹配路由 → Pre 过滤器 → 转发下游 → Post 过滤器 → 返回
📖 核心知识
Spring Cloud Gateway 基于 Spring WebFlux + Netty,核心架构由 Route(路由)、Predicate(断言)、Filter(过滤器) 三部分组成。
核心工作流程
- 请求到达:客户端请求由 Netty Server 接收。
- 路由匹配:
RoutePredicateHandlerMapping遍历所有路由,用Predicate断言匹配请求(Path、Method、Header 等)。 - 执行过滤器链:匹配成功后,请求经过
FilteringWebHandler执行过滤器链。- Pre 阶段:在转发前执行(如鉴权、日志、修改请求头)。
- 转发:由
GatewayFilter代理转发到下游服务。 - Post 阶段:响应返回后执行(如修改响应头、记录耗时)。
- 返回响应:响应经 Post 过滤器处理后返回客户端。
内置断言工厂
| 断言 | 说明 | 示例 |
|---|---|---|
Path | 路径匹配 | Path=/api/user/** |
Method | HTTP 方法 | Method=GET,POST |
Header | 请求头匹配 | Header=X-Request-Id, \d+ |
Host | Host 匹配 | Host=**.example.com |
Query | 查询参数 | Query=token, .+ |
After/Before/Between | 时间范围 | After=2025-01-01T00:00:00+08:00 |
Cookie | Cookie 匹配 | Cookie=session, .+ |
RemoteAddr | IP 匹配 | RemoteAddr=192.168.1.0/24 |
内置过滤器工厂
| 过滤器 | 说明 |
|---|---|
AddRequestHeader | 添加请求头 |
AddRequestParameter | 添加请求参数 |
RewritePath | 重写请求路径 |
StripPrefix | 去除路径前缀 |
RequestRateLimiter | 限流(需配合 Redis) |
CircuitBreaker | 熔断(需配合 Resilience4j/Sentinel) |
Retry | 重试 |
PrefixPath | 添加路径前缀 |
路由配置示例
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service # lb:// 表示从注册中心负载均衡
predicates:
- Path=/api/user/** # 路径匹配
- Method=GET,POST # 方法匹配
- Header=X-Token, .+ # 必须带 X-Token 头
filters:
- StripPrefix=1 # 去除第一层路径前缀 /api
- AddRequestHeader=X-Gateway, true
- name: RequestRateLimiter # 限流
args:
redis-rate-limiter.replenishRate: 10 # 令牌填充速率 10/s
redis-rate-limiter.burstCapacity: 20 # 令牌桶容量 20🔬 扩展知识
详情
- 【L3】为什么 Gateway 不能用 Spring MVC?Gateway 基于 WebFlux 响应式栈,Servlet 阻塞模型与 Netty 事件循环不兼容;在过滤器里调阻塞 API(如 JDBC)会卡死事件循环线程,必须用响应式客户端(R2DBC、WebClient)。
- 【L3】
lb://前缀的含义:目标 URI 交给ReactiveLoadBalancerClientFilter从注册中心解析实例,这是网关与注册中心联动的关键点;换成http://则直连固定地址。 - 【L4】
RequestRateLimiter底层是 Redis + Lua 实现的令牌桶(RedisRateLimiter),replenishRate 是稳态速率、burstCapacity 是桶容量,两值相同则不允许突发。 - 【L4】路由匹配是顺序短路的:路由按配置顺序(或 order)逐个尝试断言,命中第一个即停止,因此更具体的路由要放前面,避免被宽泛规则拦截。
🏭 实战场景
详情
生产中网关的典型职责组合:① 全局鉴权过滤器(校验 JWT、把用户 ID 写 Header 透传);② 按路由挂 RequestRateLimiter,大促前把核心接口 replenishRate 从 1000 提到 3000,非核心降到 100;③ 用 CircuitBreaker 过滤器 + fallback 路由,下游超时直接返回兜底页而不把错误透传给前端。灰度发布则通过动态路由按 Header 标签把 5% 流量导向新版本,观察无异常后逐步放量。
⚠️ 常见误区
详情
常见误区:
- ❌ “Gateway 就是 Nginx 的 Java 版” → Nginx 是通用反向代理,Gateway 是业务网关:能感知注册中心(lb://)、能挂 Java 业务过滤器、能与 Sentinel 联动,定位不同,生产中常是 Nginx 在前 Gateway 在后。
- ❌ “过滤器里随便写阻塞代码” → 会阻塞 Netty 事件循环,一个慢查询拖垮整个网关吞吐,必须用响应式 API 或 offload 到独立线程池。
- ❌ “路由顺序无所谓” → 匹配是顺序短路的,宽泛路由(如 Path=/**)放前面会吞掉后面所有更具体的路由。
- ❌ “限流只靠 RequestRateLimiter 就够” → 它是单机 Redis 令牌桶,集群级精确限流还需 Sentinel 集群流控,见本文档「Sentinel 是怎么实现集群限流的?」。
🔀 发散问题
- Q:过滤器链内部怎么排序执行? → order 洋葱模型,NettyRoutingFilter 垫底,见本文档「Gateway 过滤器链的执行顺序是怎样的?」。
- Q:路由规则能不改代码热更新吗? → 能,监听 Nacos 配置变更动态刷新,见本文档「Gateway 如何实现动态路由?」。
- Q:网关层限流熔断和调用层的分工? → 网关拦入口流量,调用层 Sentinel 拦服务间调用,见本文档「什么是服务雪崩?如何做全链路防护?」。
【中等】Gateway 如何实现动态路由?⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:网关 / 动态路由
💎 关键结论
静态路由写在配置文件中,修改需重启;动态路由监听 Nacos 等配置中心的路由定义(JSON),变更时通过 RouteDefinitionWriter 删旧加新实现热更新,无需重启。典型应用:灰度发布、故障转移、A/B 测试。
⚡记忆卡片
- 口诀:配置中心存路由,监听变更热刷新
- 关键词:RouteDefinitionWriter / RouteDefinitionLocator / Nacos 监听 / 灰度发布
- 链路:路由 JSON 存 Nacos → 监听变更 → 删旧路由 → 写新路由 → 立即生效
📖 核心知识
静态路由写在配置文件中,修改需重启。动态路由支持从 Nacos 等配置中心实时加载路由规则,无需重启。
实现方案
@Component
public class DynamicRouteListener {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private RouteDefinitionLocator routeDefinitionLocator;
/**
* 监听 Nacos 配置变更,动态刷新路由
*/
@NacosConfigListener(dataId = "gateway-routes.json", groupId = "GATEWAY_GROUP")
public void onRouteChange(String config) {
List<RouteDefinition> routes = JSON.parseArray(config, RouteDefinition.class);
// 先删除旧路由
routeDefinitionLocator.getRouteDefinitions().subscribe(route -> {
routeDefinitionWriter.delete(Mono.just(route.getId()));
});
// 再添加新路由
routes.forEach(route -> {
routeDefinitionWriter.save(Mono.just(route)).subscribe();
});
}
}动态路由的应用场景
- 灰度发布:动态调整路由规则,将部分流量导向新版本实例。
- 故障转移:某实例宕机时,动态移除指向该实例的路由。
- A/B 测试:按用户标签路由到不同版本。
🔬 扩展知识
详情
- 【L3】除了"先删后加",更稳妥的做法是按 ID 差量更新:对比新旧路由集合,只删消失的、只加新增的,避免刷新瞬间的路由真空期(短暂 404)。
- 【L3】刷新后要发布
RefreshRoutesEvent事件让缓存路由重建,否则新路由不会立即参与匹配。 - 【L4】路由定义的 JSON schema 建议加版本字段与校验,错误 JSON 会导致全量路由失效,必须先校验再应用,失败保留旧路由。
🔀 发散问题
- Q:路由规则里的断言和过滤器怎么写? → Path/Method/Header 断言 + StripPrefix 等过滤器,见本文档「Spring Cloud Gateway 的工作原理是什么?」。
- Q:灰度时怎么把流量导向指定版本? → 动态路由 + Header 断言分流,见本文档「你项目里为什么选择 Gateway 作为网关?」。
【中等】Gateway 过滤器链的执行顺序是怎样的?⭐⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:网关 / 过滤器链
💎 关键结论
过滤器链由 GlobalFilter(全局)和 GatewayFilter(路由级)统一适配为 GatewayFilter 组成,按 order 从小到大执行前半段(Pre),NettyRoutingFilter(order=Integer.MAX_VALUE)垫底负责转发,响应返回后链条逐层展开逆序执行 then(...) 后半段(Post)——响应式洋葱模型,进的越小越先,出的越大越先。
⚡记忆卡片
- 口诀:order 小的先进,路由垫底,回来倒着走
- 关键词:GlobalFilter / Ordered / NettyRoutingFilter / 洋葱模型 / Pre/Post
- 链路:请求 → order 升序执行 Pre → NettyRoutingFilter 转发 → 响应 → 逆序执行 Post
📖 核心知识
Gateway 的过滤器链由 GlobalFilter(全局)和 GatewayFilter(路由级)组成,两者最终被统一适配为 GatewayFilter 组成链条执行。
排序规则
- 过滤器实现
Ordered接口或标注@Order,order 值越小越先执行。 - 内置过滤器有固定 order,如
NettyWriteResponseFilter为 -1,转发用的NettyRoutingFilter为Integer.MAX_VALUE(保证最后执行路由)。 - 自定义 GlobalFilter 实现
GlobalFilter, Ordered指定 order;路由级过滤器按配置声明顺序包装为 OrderedGatewayFilter。
执行流程(洋葱模型)
- Pre 阶段:请求进入后按 order 从小到大执行
filter()前半段(鉴权、限流、参数改写)。 - 路由转发:
NettyRoutingFilter将请求转发到下游服务。 - Post 阶段:响应返回时链条逐层展开,逆序执行
chain.filter().then(...)之后的逻辑(统一包装响应、记录耗时)。
自定义 GlobalFilter 示例
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// Pre 阶段逻辑
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// Post 阶段逻辑
}));
}
@Override
public int getOrder() { return -100; } // 越小越先执行
}一句话总结:Gateway 过滤器链是响应式的“洋葱模型”,按 order 从小到大进、从大到小出,路由永远垫底。
🔬 扩展知识
详情
- 【L3】Post 阶段用
then(...)而非doOnSuccess的原因:then 保证在上游 Mono 完成后组合新逻辑,能拿到完整响应体做包装;若要修改响应体,需用ServerHttpResponseDecorator装饰器截持 writeWith。 - 【L4】GlobalFilter 对所有路由生效,适合鉴权、日志等横切逻辑;路由级 GatewayFilter 只对配置了它的路由生效,适合限流、重写等定向逻辑——粒度选择是设计关键。
🔀 发散问题
- Q:过滤器链是在什么时机被触发的? → 断言匹配路由后由 FilteringWebHandler 执行,见本文档「Spring Cloud Gateway 的工作原理是什么?」。
- Q:鉴权过滤器应该设多大 order? → 负数靠前,保证在业务过滤器之前拦截,见本文档「OpenFeign 的工作原理是什么?」中拦截器思路一致。
链路追踪深入
【中等】Sleuth + Zipkin 的工作原理是什么?⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:链路追踪 / Sleuth
💎 关键结论
两个核心概念:Trace(一次完整请求链,TraceId 唯一标识)和 Span(一个工作单元,SpanId + ParentSpanId 构成调用树)。流程:入口生成 TraceId → 服务间用 HTTP Header(X-B3-*)透传上下文 → 每个 Span 异步上报 Zipkin → Zipkin 存储并展示完整调用链。生产用概率/限速采样控制开销。
⚡记忆卡片
- 口诀:Trace 一条链,Span 一个活,Header 透传,异步上报
- 关键词:TraceId / SpanId / X-B3-TraceId / 采样 / Zipkin Server
- 链路:入口生成 Trace → Header 透传 → Span 异步上报 → Zipkin 聚合展示
📖 核心知识
核心概念
- Trace:一次完整的请求链路,由唯一的
TraceId标识。 - Span:链路中的一个工作单元(如一次 RPC 调用),有唯一的
SpanId,通过ParentSpanId构成调用树。
工作流程
- 入口生成 Trace:请求进入第一个服务时,Sleuth 生成
TraceId和根SpanId。 - 上下文传递:服务间调用时,通过 HTTP Header(
X-B3-TraceId、X-B3-SpanId、X-B3-ParentSpanId)传递链路上下文。 - Span 上报:每个 Span 完成后,异步上报到 Zipkin Server。
- Zipkin 聚合展示:Zipkin Server 存储 Span 数据,UI 展示完整调用链。
采样策略
| 策略 | 说明 | 适用场景 |
|---|---|---|
AlwaysSampler | 100% 采样 | 开发/测试环境 |
ProbabilityBasedSampler | 按概率采样(如 10%) | 生产环境,流量大 |
RateLimitingSampler | 限速采样(如 100 条/秒) | 生产环境,控制上报量 |
采样率配置示例
spring:
sleuth:
sampler:
probability: 0.1 # 10% 采样率
rate: 100 # 或每秒最多 100 条🔬 扩展知识
详情
- 【L3】重要演进:Spring Cloud 2022.0 版本后 Sleuth 项目不再推进,其链路追踪能力由 Micrometer Tracing 接替(配合 Brave 或 OpenTelemetry 桥接器),新项目应直接用 Micrometer Tracing + OpenTelemetry,上下文传播改用 W3C TraceContext 标准 Header(traceparent)。
- 【L3】Sleuth 的埋点范围:不仅 HTTP(RestTemplate/WebClient/Feign),还自动为异步任务(@Async、线程池)、消息(Stream/Kafka)生成/续接 Span,异步场景靠线程上下文包装器传递 TraceId。
- 【L4】采样与告警的配合:生产低采样率下故障链路可能采不到,需对错误链路强制全量采集(error-tagged sampling)。
🔀 发散问题
- Q:还有哪些追踪方案可选? → Jaeger、SkyWalking、OpenTelemetry,见本文档「Spring Cloud 支持哪些链路追踪方案?」。
- Q:SkyWalking 为什么不需要埋点? → Java Agent 字节码增强,见本文档「SkyWalking 和 Zipkin 有什么区别?」。
【中等】SkyWalking 和 Zipkin 有什么区别?⭐
🎯 目标等级:L2 | ⏱ 建议用时:10 min | 🏷 标签:链路追踪 / 选型对比
💎 关键结论
最大差异在接入方式和功能范围:SkyWalking 用 Java Agent 字节码增强零侵入,是完整的 APM(追踪 + 指标 + 告警 + 拓扑图);Zipkin 是轻量追踪系统,SDK 接入、UI 功能基础、告警需第三方。新项目推荐 SkyWalking,已有 Zipkin 的可继续用或逐步迁移。
⚡记忆卡片
- 口诀:SW 零侵入全家桶,Zipkin 轻量专注追踪
- 关键词:Java Agent / APM / 拓扑图 / 告警 / OTel 兼容
- 链路:接入方式对比 → 功能范围对比 → 存储/UI 对比 → 选型
📖 核心知识
| 维度 | SkyWalking | Zipkin |
|---|---|---|
| 接入方式 | Java Agent(字节码增强,零侵入) | SDK(需修改代码或配置) |
| 语言支持 | 多语言(Java/PHP/Python 等) | 多语言 |
| 存储后端 | ES/MySQL/H2/TiDB 等 | ES/MySQL/Cassandra 等 |
| UI 功能 | 丰富(拓扑图、告警、服务依赖) | 基础(调用链、依赖图) |
| 告警能力 | 内置告警规则 | 需第三方(如 Prometheus) |
| 性能开销 | 低(Agent 异步上报) | 低 |
| 社区活跃度 | Apache 顶级项目,活跃 | 活跃 |
| 协议标准 | 自定义 + OTel 兼容 | Zipkin 协议 + OTel 兼容 |
选型建议:新项目推荐 SkyWalking(功能全、零侵入、社区活跃),已有 Zipkin 的项目可继续使用或逐步迁移。
🔬 扩展知识
详情
- 【L3】Java Agent 零侵入的代价:字节码增强与部分框架版本兼容性需验证,升级 agent 需重启应用(或配合热部署);SDK 方案则更可控但侵入代码。
- 【L4】两者都支持 OpenTelemetry 兼容后,长期看追踪数据格式会统一到 OTLP,切换采集端的迁移成本将大幅降低。
🔀 发散问题
- Q:Sleuth 埋点和 SkyWalking Agent 埋点的数据模型一样吗? → 都是 Trace/Span 模型,上报协议不同,见本文档「Sleuth + Zipkin 的工作原理是什么?」。
- Q:追踪方案的整体格局? → Sleuth 系、Jaeger、SkyWalking、OpenTelemetry 四阵营,见本文档「Spring Cloud 支持哪些链路追踪方案?」。