Spring Cloud 从零到精通验收清单
这页用来验收你是否真正学懂 Spring Cloud。不是会背 Eureka、Feign、Gateway、Sentinel 这些名字就算会,而是要能说清:微服务为什么需要治理,一个服务名怎样变成真实 IP,一次 HTTP 调用经过哪些组件,负载均衡、超时、重试、熔断、限流分别解决什么问题,配置中心和链路追踪怎样支撑生产排查。
Spring Cloud 的主线是一句话:
服务先注册,调用方按服务名发现实例,负载均衡选择实例,HTTP 客户端发起远程调用,网关统一入口,熔断限流保护链路,配置中心统一参数,链路追踪帮助排查。
最终目标
| 能力 | 达标标准 |
|---|---|
| 零基础能懂 | 能解释 Spring Boot 和 Spring Cloud 的关系,知道为什么单体变微服务后需要治理 |
| 原理能讲 | 能画出注册发现、Feign 调用、LoadBalancer、Gateway、Sentinel、配置中心、链路追踪全过程 |
| Demo 能写 | 能写最小注册中心、Feign 调用、Gateway 路由、Sentinel 限流、Nacos 配置 Demo |
| 项目能落地 | 能放到订单、库存、支付、资产、医疗采集等商业链路里说明 |
| 故障能排查 | 能排查无实例、404、503、超时、重试风暴、限流误杀、配置不生效 |
| 面试能闭环 | 面试页放标准回答,原理回到知识点页展开 |
总路线
flowchart TD
A["为什么需要微服务治理"] --> B["Spring Boot 和 Spring Cloud 关系"]
B --> C["注册中心和服务发现"]
C --> D["OpenFeign 远程调用"]
D --> E["LoadBalancer 选择实例"]
E --> F["Gateway 统一入口"]
F --> G["超时、重试、熔断、限流、降级"]
G --> H["配置中心和动态刷新"]
H --> I["链路追踪和可观测性"]
I --> J["商业场景和生产排查"]阶段 1:为什么需要 Spring Cloud
单体到微服务后发生了什么
单体应用里,模块之间通常是本地方法调用:
orderService.createOrder(command);拆成微服务后,订单服务、库存服务、支付服务、用户服务变成多个进程,甚至部署在不同机器、不同容器、不同机房。一次“创建订单”可能变成:
flowchart TD
A["订单服务"] --> B["库存服务"]
A --> C["用户服务"]
A --> D["优惠券服务"]
A --> E["支付服务"]这时会出现单体里没有的问题:
| 问题 | 如果不治理会怎样 |
|---|---|
| 服务地址会变 | 调用方写死 IP,扩容、迁移、重启都要改配置 |
| 实例数量变多 | 不知道请求该打到哪台机器 |
| 下游会慢或挂 | 上游线程被拖住,故障扩散成雪崩 |
| 外部入口变多 | 鉴权、限流、日志、跨域到处重复 |
| 配置到处分散 | 环境混乱、改配置要发版、无法审计回滚 |
| 调用链变长 | 线上慢请求不知道卡在哪个服务 |
Spring Cloud 解决的不是“如何写业务代码”,而是“服务拆开以后如何稳定协作”。
详细知识点:Spring Cloud 从零到生产级掌握。
阶段 2:Spring Boot 和 Spring Cloud 的关系
Spring Boot 解决单个应用怎么快速启动、自动配置、暴露接口、接入数据库和监控。Spring Cloud 解决多个 Spring Boot 应用之间如何协作。
| 对比项 | Spring Boot | Spring Cloud |
|---|---|---|
| 关注对象 | 单个服务 | 多个服务组成的系统 |
| 解决问题 | 启动、配置、Web、监控、部署 | 注册发现、远程调用、负载、网关、熔断、配置中心、追踪 |
| 常见能力 | Starter、自动配置、内嵌 Tomcat、Actuator | Nacos/Eureka、Feign、LoadBalancer、Gateway、Sentinel |
| 学习顺序 | 先学 | 后学 |
Spring Cloud 的很多能力本质上也是通过 Spring Boot 自动配置接入的。例如引入 OpenFeign Starter 后,启动时扫描 @FeignClient 接口,注册代理 Bean,业务代码才能像注入普通 Bean 一样注入 Feign Client。
阶段 3:服务注册和服务发现
注册中心做什么
注册中心维护的是“服务名 -> 实例列表”的映射。
flowchart TD
A["order-service 实例1"] --> C["注册中心"]
B["order-service 实例2"] --> C
D["stock-service 实例1"] --> C
E["调用方"] --> C
C --> F["返回服务实例列表"]服务提供者启动后会注册:
- 服务名。
- IP 和端口。
- 健康状态。
- 集群、版本、权重、机房等元数据。
调用方不会每次都实时查注册中心,通常会拉取或订阅实例列表,缓存在本地。这样可以降低注册中心压力,也能在注册中心短暂不可用时继续调用已有实例。
Eureka 和 Nacos
| 对比项 | Eureka | Nacos |
|---|---|---|
| 生态 | Spring Cloud Netflix 老体系 | Spring Cloud Alibaba 常用 |
| 定位 | 注册中心 | 注册中心 + 配置中心 |
| 一致性倾向 | 偏 AP,强调可用性和客户端缓存 | 支持临时实例、永久实例、命名空间、配置管理 |
| 老项目 | 常见 | 部分项目迁移目标 |
| 新项目 | 仍可维护 | Alibaba 生态常用 |
服务发现不到怎么排查
| 现象 | 排查方向 |
|---|---|
| 注册中心没有实例 | 服务没启动、注册配置错、网络不通 |
| 控制台有实例但调用方没有 | namespace、group、region、缓存刷新、服务名不一致 |
| 有实例但不可用 | 健康状态 DOWN、权重 0、灰度过滤、IP 不可达 |
| 下线后仍被调用 | 注册剔除和客户端缓存刷新存在传播窗口 |
阶段 4:OpenFeign 远程调用
Feign 是什么
Feign 是声明式 HTTP 客户端。它让你写一个接口,运行时生成代理对象,把接口调用转换成 HTTP 请求。
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "stock-service")
public interface StockClient {
@GetMapping("/stock/{skuId}")
Integer getStock(@PathVariable("skuId") Long skuId);
}业务代码:
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final StockClient stockClient;
public OrderService(StockClient stockClient) {
this.stockClient = stockClient;
}
public void createOrder(Long skuId) {
Integer stock = stockClient.getStock(skuId);
if (stock <= 0) {
throw new IllegalStateException("stock not enough");
}
}
}Feign 调用全过程
flowchart TD
A["调用 Feign 接口方法"] --> B["JDK 动态代理拦截"]
B --> C["解析方法注解"]
C --> D["组装 RequestTemplate"]
D --> E["Encoder 序列化请求"]
E --> F["RequestInterceptor 添加请求头"]
F --> G["LoadBalancer 选择实例"]
G --> H["HTTP Client 发送请求"]
H --> I["Decoder 反序列化响应"]Feign 不是本地调用
Feign 写法像本地方法,但本质仍是远程 HTTP:
- 可能连接超时。
- 可能读取超时。
- 可能下游返回 500。
- 可能服务无实例。
- 可能序列化失败。
- 写操作重试可能导致重复扣库存或重复支付。
生产必须配置超时、错误处理、日志、traceId、熔断降级和幂等。
详细知识点:OpenFeign。
阶段 5:LoadBalancer 负载均衡
负载均衡解决什么
一个服务通常有多个实例,调用方要从实例列表里选一个。
flowchart TD
A["调用 stock-service"] --> B["获取实例列表"]
B --> C["实例1 10.0.0.1:8080"]
B --> D["实例2 10.0.0.2:8080"]
B --> E["实例3 10.0.0.3:8080"]
C --> F["LoadBalancer 选择一个"]
D --> F
E --> FRibbon 和 Spring Cloud LoadBalancer
| 对比项 | Ribbon | Spring Cloud LoadBalancer |
|---|---|---|
| 时代 | Netflix 老体系 | 当前 Spring Cloud 官方方案 |
| 核心职责 | 客户端选择实例 | 客户端选择实例 |
| 扩展点 | IRule、IPing、ServerList | ServiceInstanceListSupplier、ReactorServiceInstanceLoadBalancer |
| 当前建议 | 老项目维护要懂 | 新项目优先理解 |
负载不均为什么发生
负载均衡不是保证每台机器请求数绝对相同。负载不均可能来自:
- 实例配置不同。
- 某些请求耗时特别长。
- HTTP 连接池复用旧连接。
- 灰度或权重规则偏流。
- 热租户集中访问某类接口。
- 某台实例 Full GC 或慢 SQL,但注册中心仍认为健康。
排查不能只看请求数,要看每个实例的 QPS、P95/P99、错误率、CPU、线程池、连接池、GC、慢 SQL。
详细知识点:Spring Cloud 负载均衡。
阶段 6:Gateway 统一入口
Gateway 做什么
Gateway 是微服务统一入口,适合做:
- 路由转发。
- 登录态校验。
- 限流。
- 跨域。
- 灰度。
- 统一日志。
- traceId 透传。
- 请求和响应头处理。
不适合做复杂业务、数据库事务和领域规则。
Route、Predicate、Filter
| 概念 | 作用 |
|---|---|
| Route | 一条路由规则,决定转发到哪里 |
| Predicate | 判断请求是否命中路由 |
| Filter | 命中后在转发前后做处理 |
spring:
cloud:
gateway:
routes:
- id: asset-service
uri: lb://asset-service
predicates:
- Path=/assets/**
filters:
- StripPrefix=1lb:// 怎么变成真实地址
flowchart TD
A["请求 /assets/list"] --> B["Gateway 匹配 Route"]
B --> C["识别 uri=lb://asset-service"]
C --> D["从注册中心或缓存拿实例"]
D --> E["LoadBalancer 选择实例"]
E --> F["替换为 http://IP:Port"]
F --> G["Netty HTTP 转发"]详细知识点:Spring Cloud Gateway。
阶段 7:超时、重试、熔断、限流、降级
这几个概念经常被混在一起,必须分清边界。
| 能力 | 解决什么 | 不解决什么 |
|---|---|---|
| 超时 | 下游迟迟不返回时释放上游线程 | 不能证明下游没有执行成功 |
| 重试 | 偶发失败恢复 | 下游整体慢时会放大流量 |
| 熔断 | 下游持续异常时快速失败 | 不能恢复下游服务 |
| 限流 | 入口流量太大时保护系统 | 不是权限控制 |
| 降级 | 失败时返回兜底结果 | 不能把关键写操作伪装成功 |
重试风暴
假设入口 1000 QPS,Feign 最多重试 2 次。下游慢时,理论上可能变成 3000 QPS 打到下游:
flowchart TD
A["原始请求 1000 QPS"] --> B["第一次调用"]
B --> C["超时"]
C --> D["重试 1 次"]
D --> E["再次超时"]
E --> F["重试 2 次"]
F --> G["下游压力被放大"]写操作重试必须有幂等键、状态查询或补偿机制。支付、扣库存、创建订单不能盲目重试。
Sentinel 原理
Sentinel 把接口、方法、下游调用抽象成资源。请求进入资源时经过 SlotChain,统计 QPS、线程数、慢调用比例、异常比例,再根据规则判断是否放行。
flowchart TD
A["请求进入资源"] --> B["构建调用上下文"]
B --> C["统计 QPS、RT、异常"]
C --> D["流控规则判断"]
D --> E["熔断规则判断"]
E --> F["系统保护判断"]
F --> G{"是否通过"}
G -->|是| H["执行业务"]
G -->|否| I["BlockException"]详细知识点:Sentinel、Resilience4j现代稳定性治理、Hystrix经典组件与迁移。
阶段 8:配置中心
配置中心解决什么
配置中心不是“把 yml 放到网页上”,而是解决多服务、多环境、多实例的配置治理:
- 环境隔离。
- 权限控制。
- 审计记录。
- 灰度发布。
- 动态刷新。
- 回滚。
- 本地快照。
- 敏感配置治理。
加载流程
flowchart TD
A["应用启动早期"] --> B["读取本地引导配置"]
B --> C["连接配置中心"]
C --> D["按 Namespace、Group、DataId 定位配置"]
D --> E["拉取远程配置"]
E --> F["加入 Spring Environment"]
F --> G["自动配置和 Bean 读取最终配置"]配置中心必须尽量早加载,因为数据源、Redis、Feign 超时、线程池、Sentinel 规则等很多 Bean 创建时就要读取配置。如果加载太晚,Bean 已经按默认值创建,后续刷新不一定安全。
动态刷新不是万能
适合动态刷新的配置:
- 功能开关。
- 限流阈值。
- 灰度比例。
- 文案或低风险参数。
不适合随便刷新的配置:
- 数据源连接。
- MQ 消费组。
- 复杂线程池。
- 证书密钥。
- 影响事务和长连接状态的配置。
详细知识点:Spring Cloud 配置中心。
阶段 9:链路追踪和可观测性
链路追踪解决什么
一次请求可能经过 Gateway、订单服务、库存服务、支付服务、消息服务。没有 traceId 时,线上慢请求像在雾里找针。
flowchart TD
A["Gateway span"] --> B["order-service span"]
B --> C["stock-service span"]
B --> D["pay-service span"]
B --> E["MQ send span"]核心概念:
| 概念 | 说明 |
|---|---|
| traceId | 一次完整请求的全局 ID |
| spanId | 一段调用的 ID |
| parentSpanId | 父调用 ID |
| baggage | 跨服务传播的上下文 |
| 日志关联 | 日志里打印 traceId,方便从链路跳日志 |
老项目常见 Sleuth,新项目常见 Micrometer Tracing、OpenTelemetry、SkyWalking。
详细知识点:链路追踪与可观测性。
阶段 10:Spring Cloud Stream 和 Bus
微服务之间不只有同步 HTTP 调用,也需要异步事件。
| 技术 | 作用 | 场景 |
|---|---|---|
| Spring Cloud Stream | 抽象 Kafka/RabbitMQ 等消息中间件 | 订单事件、采集完成事件 |
| Spring Cloud Bus | 广播 Spring Cloud 系统事件 | 配置刷新广播 |
异步事件要关注最终一致、幂等、重试、死信、积压、顺序和链路追踪。不能因为用了 Stream 就忽略 MQ 的基础问题。
详细知识点:Stream 与 Bus。
阶段 11:商业场景验收
场景 1:资产详情查询
链路:
flowchart TD
A["前端"] --> B["Gateway"]
B --> C["asset-service"]
C --> D["dict-service"]
C --> E["permission-service"]
C --> F["Redis 或 MySQL"]验收问题:
- Gateway 做哪些事?哪些事不能放 Gateway?
- asset-service 调 dict-service 时,服务名如何变成真实 IP?
- dict-service 慢时,asset-service 怎么超时、熔断、降级?
- 如何用 traceId 定位慢在字典、权限还是数据库?
场景 2:医疗采集任务
链路:
- 采集服务从配置中心读取医院接口地址、开关、线程池参数。
- 采集服务调用字典服务和资产服务。
- 医院接口慢时不能无限重试。
- 失败批次记录原因,后续补偿。
- 关键接口按医院编码或租户限流。
验收问题:
- 配置中心改线程池参数为什么不能随便动态刷新?
- 医院接口超时后能不能立刻重试?如何避免放大故障?
- Sentinel 热点参数限流为什么适合按
hospitalCode?
场景 3:服务上线和优雅下线
流程:
flowchart TD
A["新实例启动"] --> B["通过健康检查"]
B --> C["注册到注册中心"]
C --> D["调用方刷新实例缓存"]
D --> E["开始接流量"]
E --> F["下线前置 Readiness 不可用"]
F --> G["注册中心摘除"]
G --> H["等待缓存刷新和请求处理完成"]
H --> I["关闭进程"]验收问题:
- 为什么服务下线后短时间还可能被调用?
- K8s readiness、preStop、graceful shutdown 各自解决什么?
- HTTP 连接池为什么会继续影响调用?
阶段 12:生产排查
服务调用失败决策树
flowchart TD
A["服务调用失败"] --> B{"是否经过 Gateway"}
B -->|是| C["看 Gateway 状态码"]
C --> D["404 查路由 Predicate"]
C --> E["503 查注册中心和实例"]
C --> F["504 查下游超时"]
B -->|否| G["看 Feign 错误"]
G --> H["无实例查服务名和 namespace"]
G --> I["连接超时查网络和端口"]
G --> J["读取超时查下游耗时"]高频故障
| 故障 | 排查重点 |
|---|---|
| Feign 无实例 | 服务名、Namespace、Group、实例健康、本地缓存 |
| Gateway 404 | Route 是否命中、Path/Method/Header、StripPrefix |
| Gateway 503 | lb:// 服务名、实例列表、健康状态、灰度过滤 |
| Read timeout | 下游线程池、慢 SQL、锁、GC、第三方接口 |
| 连接池耗尽 | HTTP Client 连接池、每路由连接数、连接泄漏 |
| 负载不均 | 实例指标、权重、灰度、长连接、慢实例 |
| Sentinel 拦截 | Flow/Degrade/Param/System/Authority 具体异常类型 |
| 配置不生效 | Namespace、Group、DataId、profile、优先级、Bean 是否刷新 |
| traceId 丢失 | 线程池、异步、MQ、网关过滤器顺序、日志格式 |
阶段 13:面试闭环
flowchart TD
A["面试题"] --> B["标准回答"]
B --> C["回知识点页看原理"]
C --> D["画调用链路"]
D --> E["说商业场景"]
E --> F["补故障排查"]| 面试题 | 必须答到的深度 |
|---|---|
| Spring Cloud 是什么 | 微服务治理工具集,不是单个框架 |
| 一次调用怎么走 | Gateway、Feign、注册中心、LoadBalancer、HTTP Client、Sentinel、Tracing |
| 注册中心做什么 | 服务注册、续约、发现、本地缓存、健康状态、元数据 |
| Feign 原理 | 动态代理、注解解析、编码解码、拦截器、HTTP Client |
| 负载均衡 | 服务实例列表、过滤、选择算法、Ribbon/LoadBalancer 差异 |
| Gateway | Route、Predicate、Filter、lb://、Netty 转发 |
| 熔断限流 | 超时、重试、熔断、限流、降级边界 |
| 配置中心 | 启动早期加载、Environment、动态刷新边界 |
| 链路追踪 | traceId、span、上下文传播、日志关联 |
| 生产排查 | 无实例、404、503、超时、配置不生效、traceId 丢失 |
面试标准回答看:Spring Cloud 面试知识点。
最终验收题
- Spring Boot 和 Spring Cloud 的区别是什么?
- 为什么微服务不能写死 IP?
- 注册中心保存哪些信息?
- 调用方为什么要缓存实例列表?
- Eureka 为什么偏 AP?
- Nacos 的 Namespace、Group、DataId 分别解决什么?
- Feign 为什么不是本地调用?
- Feign 接口方法如何变成 HTTP 请求?
- Encoder、Decoder、RequestInterceptor 各自做什么?
- Ribbon 和 Spring Cloud LoadBalancer 区别是什么?
- 客户端负载均衡和服务端负载均衡区别是什么?
lb://service-name如何变成真实 IP?- Gateway 的 Route、Predicate、Filter 怎么协作?
- 为什么网关不能写复杂业务?
- 超时、重试、熔断、限流、降级分别解决什么?
- 为什么写操作不能盲目重试?
- Sentinel 的资源、规则、滑动窗口、SlotChain 怎么理解?
- 配置中心为什么要启动早期加载?
- 动态刷新为什么不是万能的?
- 配置不生效怎么排查?
- traceId 和 spanId 区别是什么?
- 异步线程或 MQ 为什么可能丢 traceId?
- 服务下线后为什么还会被调用?
- Feign
No instances available怎么排查? - Gateway 404、503、504 分别怎么排查?
本章小结
Spring Cloud 学习要按“治理问题”而不是“组件名字”推进。注册中心解决服务在哪里,Feign 解决怎么声明式调用,LoadBalancer 解决选哪台实例,Gateway 解决入口治理,Sentinel/Resilience4j 解决故障保护,配置中心解决参数治理,链路追踪解决跨服务排查。能把这些组件串成一条真实请求链路,才算真正理解 Spring Cloud。
