楼层: 首页/ 软件技术/ Spring Cloud 微服务/ 熔断降级:Sentinel
六

熔断降级:Sentinel

Flow Control & Circuit Breaking with Sentinel

第四个救命问题:一个服务挂了,不能拖死全家。下单服务要调用户服务查信息,结果用户服务因为数据库挂了卡住不返回,下单服务的线程全堵在那等,最后下单服务也跟着崩。这叫"雪崩"。Sentinel 就是刹车——访问量太大就限流,下游挂了就熔断,不让故障扩散。

论熔断状态机:关闭 → 打开 → 半开

关闭(Closed):正常状态,请求正常通过。

打开(Open):错误率/慢调用比例超阈值,熔断器打开。之后的请求不再真的去调下游,直接走降级逻辑(返回默认值)。

半开(Half-Open):过了一段时间(比如 5 秒),放一个请求过去试探。成功了就关闭熔断器恢复正常;失败了继续打开。

大白话:就像家里保险丝——电流太大自动跳闸,过一会儿手动推上去试一下,还过载就继续跳。别硬顶着烧电线。

接入 Sentinel:控制台 + 依赖

pom.xml 加 Sentinel 依赖

<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!-- Sentinel 与 OpenFeign 集成,让 Feign 调用也走熔断 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

application.yml 告诉业务服务"Sentinel 控制台在哪"

spring: cloud: sentinel: transport: dashboard: localhost:8080 # Sentinel 控制台地址 port: 8719 # 业务服务跟控制台通信的端口 eager: true # 启动就连控制台,别等第一次调用 # 让 Feign 集成 Sentinel feign: sentinel: enabled: true

@SentinelResource 定义资源与降级方法

在要保护的方法上加 @SentinelResource,指定一个 fallback 方法——熔断或限流时就调这个兜底方法,返回默认值。

订单服务里保护"查用户信息"这个方法

import com.alibaba.csp.sentinel.annotation.SentinelResource; import org.springframework.stereotype.Component; @Component public class OrderService { // value = 资源名,Sentinel 控制台里就叫这个名字 // fallback = 降级方法名,熔断/限流时调它 @SentinelResource(value = "queryUser", fallback = "queryUserFallback") public UserDTO queryUser(Long userId) { // 正常逻辑:Feign 远程调用户服务 return userClient.getUserById(userId); } // 降级方法:参数和返回值必须跟原方法一致,方法名跟上面 fallback 对应 public UserDTO queryUserFallback(Long userId) { UserDTO dto = new UserDTO(); dto.setId(userId); dto.setName("用户信息暂不可用(降级返回)"); return dto; } }
规则类型大白话
流控规则(QPS)每秒请求数超过阈值就限流,超出的请求直接拒绝。
降级规则(RT)响应时间超过阈值、或异常比例太高,就熔断一段时间。
热点参数限流针对某个参数值单独限流,比如用户 ID=1234 访问太频繁就限制他。
系统保护整台机器 CPU、负载太高时,自动整体限流,保命用。

热点参数限流:只治那个闹事的人

全站 QPS 没超,但某个 orderId=999(黄牛、爬虫)一秒刷几百次。普通流控是"整个接口一刀切",热点参数限流能针对某一个参数值单独设阈值,只治他,不影响其他正常用户。

// ① 方法上标 @SentinelResource,第二个参数 orderId 就是热点参数 @SentinelResource(value = "getOrder", blockHandler = "block") public OrderDTO getOrder(Long userId, Long orderId) { ... } // ② 控制台配:簇点链路 → getOrder → 热点参数 → 参数索引选 1(orderId) // - 单机阈值:普通参数 QPS=10 // - 例外项:orderId=999 单独设 QPS=2,这个黄牛被单独按住

论参数索引怎么数

方法参数从 0 开始数。getOrder(Long userId, Long orderId) 里 userId 是索引 0、orderId 是索引 1。想对哪个参数做热点限流,控制台就选那个索引。热点限流只对参数类型支持的方法生效,配合 @SentinelResource 用。

系统规则:整机快扛不住时的最后一道闸

前面的流控、熔断都是"按接口"治。系统保护(系统规则)是"按整台机器"治:CPU 使用率、系统 Load、入口 QPS 超过阈值,Sentinel 直接拒绝新请求,保住机器不被打死。控制台"系统规则"里配。

维度人话
CPU 使用率机器 CPU 超过阈值(如 80%),开始限流。
系统 Load系统负载过高时限流(适合不关注 CPU 细节的场景)。
入口 QPS / 平均 RT单机入口总 QPS 或平均响应时间超阈值,整体拒绝。

完整案例:@SentinelResource + blockHandler + fallback

Sentinel 里两个兜底方法容易混:blockHandler 管"被限流/熔断挡住"的情况,fallback 管"业务方法抛异常"的情况。两个一起用,谁触发谁兜底。

一个方法同时配 blockHandler 和 fallback

@SentinelResource( value = "getOrder", blockHandler = "getOrderBlock", // 被流控/熔断时调它 fallback = "getOrderFallback") // 抛异常时调它 public OrderDTO getOrder(Long orderId) { return orderDao.selectById(orderId); // 查库,可能抛异常 } // blockHandler 必须额外加一个 BlockException 参数,签名要对得上 public OrderDTO getOrderBlock(Long orderId, BlockException ex) { OrderDTO dto = new OrderDTO(); dto.setNote("手速太快,被限流了,喝口水再来"); return dto; } public OrderDTO getOrderFallback(Long orderId, Throwable t) { OrderDTO dto = new OrderDTO(); dto.setNote("订单系统开小差,稍后再试"); return dto; }

控制台里怎么配规则(操作路径)

# 启动 Sentinel 控制台(默认 8080) # 浏览器开 http://localhost:8080 ,看到 order-service 已连上来 # 1. 流控规则: # 簇点链路 → getOrder → 流控 → 单机阈值 QPS = 5 → 确定 # 意思:一秒超过 5 次访问 getOrder,多的走 blockHandler # 2. 降级规则: # 降级 → 新增 → 异常比例 0.5(错误率超 50%)→ 熔断 10 秒 # 3. 热点参数: # 热点参数 → 给 orderId 设规则,比如 id=12345 的用户单独限流

验证效果

# 正常: GET /order/getOrder?orderId=1 → 正常订单数据 # 用压测工具一秒打 10 次,超过 QPS=5 的部分: GET /order/getOrder?orderId=1 → {"note":"手速太快,被限流了,喝口水再来"} # 故意把数据库停了让方法抛异常: GET /order/getOrder?orderId=1 → {"note":"订单系统开小差,稍后再试"}
Sentinel 规则默认不持久化

你在 Sentinel 控制台配的流控规则,默认只存在内存里,服务重启就没了。生产环境必须把规则持久化到 Nacos——控制台改规则时同步写 Nacos,业务服务启动时从 Nacos 拉。网上有现成的"规则持久化到 Nacos"的配置写法,搜 Sentinel Nacos rule persistent 即可,以官方 wiki 最新说明为准。

本章面试题

面试快答 · 熔断降级与限流

Q1. 限流的常见算法有哪些?

参考答案

① 计数器(固定窗口,临界突发问题);② 滑动窗口;③ 漏桶(匀速消费,平滑但不允许突发);④ 令牌桶(匀速补令牌,允许一定突发,最常用)。Gateway 的 RequestRateLimiter 和 Sentinel 的排队等待都基于令牌桶思想。

Q2. 熔断和限流有什么区别?

参考答案

限流是"防自己被打死"——请求太多,多的直接拒绝,保护自己;熔断是"防被下游拖死"——下游错误率太高,干脆不调它、走降级,保护下游也保护自己线程池。一个是入口节流,一个是出口断连。

Q3. blockHandler 和 fallback 到底谁管谁?

参考答案

blockHandler 只在被 Sentinel 限流/熔断(BlockException)时触发;fallback 在方法本身抛业务异常时触发。签名上 blockHandler 方法要额外加一个 BlockException 参数。

Q4. 为什么会雪崩?怎么从架构上防?

参考答案

同步调用 + 下游变慢 → 调用方线程全阻塞在等下游 → 线程池耗尽 → 自己也崩。防线:超时(别无限等)、熔断(下游挂了别再打)、限流(别让自己过载)、降级(返回兜底)、异步(MQ 解耦)。