Spring Cloud 微服务
校招考察频度:☆ 中频(企业级必备·大厂进阶标配) 一句话速记:Nacos 5秒心跳管服务注册感知,OpenFeign 动态代理组装 HTTP 调远程;Gateway 异步响应式网关统管鉴权路由防刷,Sentinel 滑动窗口精细熔断限流保高可用,Nacos Config 长轮询监听配
@RefreshScope动态热刷新。
在现代 Java 后端开发中,业务早已从早期的单体应用演进为分布式微服务集群。Spring Cloud 提供了开箱即用的微服务全家桶解决方案。目前工业界(尤其是国内互联网与科技企业)已全面转向以 Spring Cloud Alibaba 为代表的新一代微服务架构体系。本篇将全面拆解服务注册发现、RPC 远程调用、API 网关、熔断限流以及分布式配置中心五大核心支柱。
一、0 基础生动类比:现代化智慧城市的运行网络
如果把大型电商或金融系统比作一座拥有数千万人口的现代化智慧大都市:
| 微服务组件 | 智慧城市角色 | 现实生产作用 |
|---|---|---|
| Nacos 注册中心 | 市政**“企业工商登记与黄页中心”** | 各个微服务(订单、库存、支付)开门营业先在此登记 IP 和端口;定期汇报心跳,若倒闭挂掉及时从黄页摘牌。 |
| OpenFeign 远程调用 | 政府各部门之间的**“专用对讲专线与调度员”** | 订单科要调用户科的接口,无需手写 HTTP 请求地址,只要对着 Java 接口说一句话,自动完成网络寻址并直连发信。 |
| LoadBalancer 负载均衡 | 办公大厅前的**“取号分流引导员”** | 用户科开了 10 个办事窗口(10 个微服务节点),分流员负责轮询或根据负载高低把请求均匀分发给空闲窗口。 |
| Spring Cloud Gateway | 城市的**“中心海关收费站与安检安保口”** | 所有外部车辆(用户前端请求)进城必须经过网关:查验身份证(JWT 令牌鉴权)、交通安检防爆(防黑客攻击/跨域)、分道驶入对应城区(路由断言)。 |
| Sentinel 流量防护 | 景区的**“限流旋转闸机与应急安全泄洪阀”** | 节假日暴增 10 倍客流时,按每秒承载限速放行(限流);某个景点发生火灾踩踏(接口故障),立即拉下警报熔断,避免全城瘫痪。 |
| Nacos Config 配置中心 | 市政**“统一广播电台与政令发布大屏”** | 市长发布一条新的通行令(修改生产配置参数),全城各个部门通过广播(长轮询)在毫秒内感知并自动更新执行,完全无需停业整顿。 |
二、微服务核心全景架构图
Spring Cloud 微服务典型全景调用链路
三、服务注册与发现:Nacos 核心机制
服务实例启动后,动态向注册中心汇报自己的网络地址;服务调用方通过服务名拉取健康的实例列表。
1. Nacos vs 传统 Eureka 核心对比
| 核心维度 | Nacos | Eureka(已宣布维护模式) | 考察重要度 |
|---|---|---|---|
| 一致性协议 | AP 模式(默认临时实例)+ CP 模式(持久实例 Raft 协议) | 纯 AP 模式(Eureka 强调高可用,自我保护机制) | ★ 核心必问 |
| 健康检查方式 | 客户端主动心跳汇报 + 服务端主动反向探测 | 仅客户端心跳(30 秒一次心跳,感知下线慢) | ★ 重点考点 |
| 服务下线感知 | 毫秒级极速感知(UDP 广播主动推送变更 + 客户端定时轮询双保险) | 分级三层缓存机制,下线通知可能需要 60~90 秒延迟 | 生产致命缺陷 |
| 功能整合 | 注册中心 + 分布式配置中心一体化整合 | 仅注册中心,配置中心需额外引入 Spring Cloud Config | 架构选型优势 |
2. Nacos 临时实例心跳保活时序
在默认的 AP 模式下(微服务实例为临时实例 ephemeral = true):
- 心跳注册:微服务启动,向 Nacos Server 发起注册,并开启一个客户端后台定时任务,每隔 5 秒向 Nacos 发送一次心跳包。
- 标记不健康:若 Nacos Server 超过 15 秒未收到心跳,Nacos 会将该服务节点标记为非健康状态(Healthy = false),服务调用方拉取列表时不会路由给它。
- 剔除下线:若超过 30 秒仍未收到心跳,Nacos Server 会直接从服务注册表中彻底清除该实例元数据,并通过 UDP 广播即时通知所有订阅了该服务的客户端更新本地缓存列表。
四、声明式远程调用:OpenFeign 与负载均衡
OpenFeign 让编写 Java HTTP 客户端变得像调用本地方法一样简单。
1. OpenFeign 底层调用原理解析
┌─────────────────────────────────────────────────────────────┐
│ 1. 启动扫描: 扫描 @FeignClient 注解,为每个接口生成 JDK 动态代理│
└──────────────────────────────┬──────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. 方法解析: 解析方法上的 @GetMapping / @PathVariable 等注解 │
│ 组装 RequestTemplate (包含 HTTP 方法、URI、Header、Body) │
└──────────────────────────────┬──────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. 服务寻址与负载均衡: │
│ 从 URL 中的服务名 (如 "user-service") 触发 LoadBalancer │
│ 从本地缓存的服务列表根据算法(轮询/权重)选择一台健康的 IP:Port │
└──────────────────────────────┬──────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. 发起物理 HTTP 请求: │
│ 交由底层 HTTP 客户端 (OkHttp / Apache HttpClient) 建立连接 │
└─────────────────────────────────────────────────────────────┘2. 生产关键避坑与性能优化
- 必须替换默认的
HttpURLConnection:- OpenFeign 默认底层的 HTTP 客户端实现是 JDK 自带的
HttpURLConnection,默认完全没有连接池,每次请求都完整经历三次握手和四次挥手! - 生产调优:必须引入
feign-okhttp或httpclient5,并在配置文件开启连接池复用长连接:feign: okhttp: enabled: true # 启用 OkHttp 高性能连接池
- OpenFeign 默认底层的 HTTP 客户端实现是 JDK 自带的
- 合理设置连接与读取超时:
- 生产环境中远程微服务可能因为 GC、锁等待或网络抖动响应变慢,不设置超时会导致大量 Tomcat 线程卡在 Feign 请求上最终拖垮整个系统:
feign: client: config: default: connectTimeout: 2000 # 建立 TCP 连接超时 2s readTimeout: 5000 # 获取响应读取超时 5s
- 生产环境中远程微服务可能因为 GC、锁等待或网络抖动响应变慢,不设置超时会导致大量 Tomcat 线程卡在 Feign 请求上最终拖垮整个系统:
五、微服务统一网关:Spring Cloud Gateway
Gateway 处于整个微服务体系的最前端,负责网络请求的统一入口接入。
1. 架构核心与设计优势
- 技术底座:基于 Spring 5.0、Spring Boot 2.0 与 Project Reactor 响应式编程,底层完全由 Netty 驱动,采用非阻塞异步 I/O 模型。
- 与老一代 Zuul 1.x 对比:Zuul 1.x 基于阻塞式 Servlet 线程池(一个请求独占一个线程),高并发突发时线程池极易被打满;Gateway 基于 Netty 事件循环少量 Worker 线程承载数万 QPS 极速转发。
2. Gateway 三大核心要素
- Route(路由):网关的基本构建块,包含 ID、目标 URI、一组断言(Predicate)和一组过滤器(Filter)。
- Predicate(断言):匹配 HTTP 请求的条件规则。满足断言条件才将请求转发到对应目标 URI。
- 常用断言:
Path=/api/order/**、Method=GET,POST、Header=X-Request-Id, \d+、After=...(时间之后)。
- 常用断言:
- Filter(过滤器):请求转发前(Pre)或收到微服务响应后(Post)执行的生命周期钩子。
3. 全局鉴权过滤器生产实战代码
@Component
@Slf4j
public class GlobalAuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 1. 白名单免密放行(登录接口、注册接口、验证码)
if (path.startsWith("/api/auth/login") || path.startsWith("/api/auth/register")) {
return chain.filter(exchange);
}
// 2. 提取 Header 中的 Authorization 令牌
String token = request.getHeaders().getFirst("Authorization");
if (StringUtils.isEmpty(token) || !validateJwt(token)) {
// 3. 校验失败,拦截并直接响应 401 Unauthorized
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 4. 解析 Token 中的 userId 并放入 Header 传递给下游微服务
String userId = parseUserIdFromToken(token);
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
@Override
public int getOrder() {
return -100; // 优先级最高,率先拦截鉴权
}
}六、高可用流量防护与熔断降级:Sentinel
当微服务链路中的某个下游服务(如支付服务)响应极慢或宕机时,调用方的线程会被大量阻塞,可能产生雪崩效应(Cascading Failure)。Sentinel 是阿里开源的专业流量防护与熔断卫士。
1. Sentinel vs Hystrix 架构演进
| 维度 | Sentinel | Hystrix(Netflix 已停止维护) |
|---|---|---|
| 隔离策略 | 信号量隔离(默认并发线程数限流),极低内存与线程切换开销 | 线程池隔离(给每个依赖分配独立线程池),线程上下文切换开销巨大 |
| 限流统计底层 | 高效滑动窗口算法(LeapArray),精确切分纳秒时间窗 | 基于滑动窗口(RxJava 实现) |
| 实时控制台 | 完善的控制台,支持动态下发规则并持久化到 Nacos | 简陋的 Hystrix Dashboard |
2. 滑动窗口(LeapArray)限流核心原理
Sentinel 弃用了简单的固定计数器(存在临界突刺问题),采用滑动窗口算法:
- 将一个统计周期(如 1 秒)细分为若干个更小的时间窗口(Bucket,例如切分为 5 个,每个 200ms)。
- 随着真实物理时间的推进,时间指针向前滑动,淘汰过期的最老窗口,仅累计当前滑动窗口跨度内的所有请求总数。
- 优点:避免了固定窗口在临界点(如第 59.9s 与 60.1s)同时涌入 2 倍限流阈值请求的破防缺陷,统计极其平滑、精准。
3. 熔断降级三大指标与状态机切换
Sentinel 监控接口调用的健康情况,当异常达到阈值时触发熔断:
- 慢调用比例(Slow Request Ratio):响应时间超过指定 RT 的请求数占比超过阈值。
- 异常比例(Error Ratio):抛出异常的请求数占比超过阈值(如 50%)。
- 异常数(Error Count):在统计时间内发生异常的绝对次数。
┌─────────────────────────────────────────────────────────────┐
│ 正常状态: CLOSED (熔断器关闭) │
│ 所有请求正常透传执行 │
└──────────────────────────────┬──────────────────────────────┘
│ 达到慢调用比例/异常阈值,触发熔断
▼
┌─────────────────────────────────────────────────────────────┐
│ 熔断状态: OPEN (熔断器开启,持续熔断时长如 10 秒) │
│ 请求直接被拦截,快速执行 fallback 本地兜底逻辑,避免雪崩 │
└──────────────────────────────┬──────────────────────────────┘
│ 熔断时间到达
▼
┌─────────────────────────────────────────────────────────────┐
│ 探测状态: HALF-OPEN (半开探测状态) │
│ 放行一个探测请求: │
│ • 若该请求调用成功 ➔ 熔断器恢复为 CLOSED (完全愈合) │
│ • 若该请求依然失败 ➔ 熔断器重新进入 OPEN 并重置熔断时长 │
└─────────────────────────────────────────────────────────────┘七、分布式配置中心与动态刷新:Nacos Config
1. 配置实时动态刷新(@RefreshScope)底层原理
在业务代码中,经常在类上标注 @RefreshScope 实现配置热更新:
@RefreshScope
@RestController
public class PayController {
@Value("${pay.channel.discount:1.0}")
private Double discount;
}- 动态刷新实现原理:
@RefreshScope本质是一个自定义的 Bean Scope 作用域(类似于 singleton、prototype)。- 所有标注了
@RefreshScope的 Bean,被放入一个独立的专属缓存池GenericScope中。 - 当 Nacos 推送新的配置事件后,Spring Cloud 发送
RefreshEvent事件。 ContextRefresher收到事件后,会清空当前 GenericScope 缓存池中所有的 Bean 实例。- 下次外部 HTTP 请求再次调用该 Controller 时,容器发现作用域缓存为空,会重新读取最新配置并重新实例化该 Bean。
2. 客户端长轮询(Long Polling)实时感知机制
Nacos 客户端并不是无休止地向服务端发送高频轮询(耗尽服务端 CPU),也不是纯粹维持常驻 TCP 长连接(大量闲置客户端浪费连接数),而是采用长轮询机制:
- 客户端发起长轮询 HTTP 请求,携带自身本地配置的 MD5 签名,并声明超时等待时间(默认 30 秒)。
- 如果服务端配置未发生任何变动:服务端并不立即响应,而是挂起该 HTTP 请求,直到接近 30 秒超时前返回 304 告知未修改,客户端立刻发起下一次长轮询。
- 如果期间管理员在 Nacos 平台修改了配置:服务端在毫秒级检测到 MD5 发生改变,立即唤醒挂起的客户端长轮询连接,立刻返回发生变动的 DataId,客户端立刻拉取最新配置并发布刷新事件!
八、校招高频面试追问
- 分布式系统下的微服务雪崩效应是如何发生的?如何防止?
- 答:
- 成因:微服务架构中存在深层的调用链条(A -> B -> C)。当底层服务 C 响应极慢或发生故障时,上游服务 B 在等待响应的过程中不会释放自身的工作线程(Tomcat 线程池),导致 B 的线程池被快速耗尽;继而导致服务 A 的请求全部阻塞在 B 上。最终局部单点故障沿着调用依赖链路向上蔓延扩散,导致整个分布式系统全盘瘫痪。
- 防范:引入 Sentinel / Hystrix 采取快速失败(超时控制)、线程/信号量隔离、熔断降级(慢调用/异常数拦截并走 Fallback 兜底数据)、以及服务集群扩容与限流削峰。
- 答:
- Nacos 在 AP 和 CP 模式之间如何切换?生产中怎么选?
- 答:
- Nacos 支持通过注册实例时的
ephemeral字段指定临时实例(AP)或持久化实例(CP)。 - 选型原则:
- 绝大部分业务微服务注册必须选 AP 模式(临时实例,默认模式)。微服务节点众多,随时可能因网络抖动或弹性伸缩上下线,保证注册中心的可用性(Availability)与高并发读写是第一诉求,即便极短时间内列表存在短暂元数据不一致,客户端结合重试机制也能容忍。
- 核心基础设施或强一致性存储节点(如数据库/分布式协调器)可选用 CP 模式(持久化实例)。借助 Raft 协议保证所有节点强一致性,宁可不可用也绝不能读到脏节点信息。
- Nacos 支持通过注册实例时的
- 答:
- Ribbon / LoadBalancer 的主要负载均衡策略有哪些?
- RoundRobinRule:轮询策略(默认基础策略),按顺序轮流分发。
- RandomRule:随机策略,均匀随机分配。
- WeightedResponseTimeRule:加权响应时间策略,响应越快的节点权重越高,分发的请求越多。
- BestAvailableRule:最佳可用策略,优先选择当前并发连接数最少、故障率最低的节点。