Skip to content

Sentinel

Sentinel 是面向分布式系统的流量治理组件,常用于限流、熔断降级、热点参数保护和系统自适应保护。它的目标不是让系统永远不出错,而是在流量过大或依赖异常时,保护核心服务不被拖垮。

本页负责从零建立概念和用法。CtSphContextEntry、节点树、Slot真实顺序、LeapArray窗口复用、Warm Up、匀速排队、集群流控、动态规则控制面、异步上下文和生产恢复请继续阅读:

一句话理解:Sentinel 负责决定一个请求能不能继续执行,以及失败时返回什么降级结果。

阅读入口:一次调用如何被规则拦截

按“资源埋点 → 规则匹配 → 滑动窗口统计 → 流控/熔断判断 → fallback”阅读。先分清限流、熔断和降级,再学习热点参数、集群流控和规则持久化。

为什么需要 Sentinel

场景没有保护时使用 Sentinel 后
秒杀接口突发流量请求全部打进业务和数据库超过阈值直接限流
下游服务变慢调用线程堆积,拖垮上游熔断一段时间,快速失败
某个用户疯狂刷接口影响其他正常用户按参数或用户限流
系统 CPU 过高所有接口继续接流量触发系统保护

请求处理流程

mermaid
flowchart TD
    A["请求进入接口"] --> B["识别 Sentinel 资源名"]
    B --> C["统计实时 QPS、线程数、异常、耗时"]
    C --> D["依次执行流控与熔断判断"]
    D --> E["通过则执行业务,阻断则抛异常"]
    E --> F["记录业务结果并退出资源"]

Sentinel 的核心原理:资源、规则、统计、拦截

Sentinel 不是简单的 if (qps > 100) return。它会围绕“资源”维护实时统计,再用规则判断请求是否允许通过。

mermaid
flowchart TD
    A["进入受保护资源"] --> B["建立Context"]
    B --> C["NodeSelector"]
    C --> D["ClusterBuilder"]
    D --> E["Statistic"]
    E --> F["Authority"]
    F --> G["System"]
    G --> H["Flow"]
    H --> I["CircuitBreaker"]
    I --> J["执行业务或阻断"]

学习时不用死背每个 Slot 的源码,但要理解这条主线:

text
先识别资源,再采集实时指标,再按规则判断是否放行。

SlotChain 每一段到底在干什么

把 SlotChain 想成一条“请求安检通道”。请求不是直接进入业务方法,而是先经过多个检查点。每个检查点只负责一类事情,组合起来完成流量治理。

Slot可以怎么理解主要作用如果没有它会怎样
NodeSelectorSlot建调用路径记录当前资源在什么调用入口下被访问分不清同一个资源从哪个入口进来
ClusterBuilderSlot建统计节点为资源创建全局统计节点 ClusterNode无法聚合资源整体 QPS、异常、RT
StatisticSlot计数器统计通过、拒绝、异常、耗时、并发线程数后续规则没有实时数据可判断
FlowSlot限流门卫根据 QPS、线程数、关联资源等判断是否限流大流量会全部打进业务
DegradeSlot熔断门卫根据慢调用比例、异常比例、异常数判断是否熔断下游持续慢时仍继续调用
SystemSlot系统保护根据系统 load、CPU、入口 QPS、线程数保护整体系统机器快被打满时还继续接流量
AuthoritySlot黑白名单根据调用来源做简单授权控制无法基于来源做拦截

关键是:StatisticSlot 通常既要在请求进入时记录线程数,也要在请求结束时记录耗时和异常。熔断规则并不是凭空判断,而是基于这些统计结果。

mermaid
flowchart TD
    A["请求进入资源"] --> B["前置统计并发线程数"]
    B --> C["FlowSlot 判断是否限流"]
    C --> D["DegradeSlot 判断是否熔断"]
    D --> E["业务方法执行"]
    E --> F["记录 RT、成功、异常"]
    F --> G["更新滑动窗口指标"]

如果面试问 Sentinel 原理,不要只说“它能限流熔断”。更好的回答是:

text
Sentinel 把接口、方法、外部调用抽象成资源,请求进入资源时会经过 SlotChain。SlotChain 先建立调用上下文和统计节点,再用滑动窗口统计 QPS、RT、异常、线程数,最后由 FlowSlot、DegradeSlot、SystemSlot 等规则判断是否放行。通过则执行业务,不通过抛出 BlockException,由 blockHandler 或统一异常处理返回限流降级结果。

滑动窗口为什么能统计实时 QPS

限流和熔断都需要实时指标。Sentinel 会把一段时间切成多个小窗口,每个窗口记录请求数量、异常数、耗时等。

mermaid
flowchart TD
    A["1秒统计周期"] --> B["切分多个小窗口"]
    B --> C["各桶记录通过、拒绝、异常和RT"]
    C --> D["汇总最近周期内的有效桶"]

当时间向前推进,旧窗口会被淘汰,新窗口加入。这样比“每秒整点清零”更平滑,不会在边界处出现统计跳变。

如果不了解滑动窗口,就很难解释为什么 Sentinel 能根据最近一段时间的异常比例、慢调用比例做熔断。

核心概念

概念说明
Resource受保护的资源,可以是接口、方法、外部调用
Rule规则,例如 QPS 限流、慢调用熔断
BlockExceptionSentinel 拦截请求时抛出的异常
Fallback业务异常或降级时返回的兜底结果
DashboardSentinel 控制台,用于查看资源和配置规则

常见规则

规则解决的问题示例
流控规则控制 QPS 或并发线程数登录接口每秒最多 100 次
熔断规则下游慢或异常时快速失败库存服务异常比例过高时熔断
热点参数规则针对某个参数值单独限流商品 ID 为爆款时限流
系统规则根据系统负载保护整体服务CPU 或入口 QPS 超阈值
授权规则简单黑白名单访问控制只允许指定来源调用

流控规则讲细:QPS、并发、关联、链路

流控不是只有“每秒最多多少请求”。Sentinel 的流控可以按不同角度保护系统。

类型判断依据适合场景例子
QPS 限流单位时间通过请求数接口很快,但流量可能突增查询接口每秒最多 500 次
并发线程数限流当前正在执行的线程数接口慢、导出、外部接口调用导出接口最多同时 10 个
关联流控关联资源压力过大时限制当前资源保护写操作或核心资源支付提交压力大时限制查询刷新
链路流控只限制某个入口链路同一方法被多个入口调用管理端不限,开放接口限

QPS 限流和并发线程数限流怎么选

很多线上误配来自“慢接口只配 QPS”。例如一个导出接口每秒只有 5 个请求,但每个请求执行 60 秒,最终会同时占用 300 个执行位置。

text
并发数约等于 QPS * 平均耗时秒数

如果接口平均耗时 60 秒,QPS 是 5:

text
并发数 = 5 * 60 = 300

这时 QPS 看起来不高,但线程和连接已经被占满。慢接口应该优先考虑并发线程数限流,必要时改成异步任务。

mermaid
flowchart TD
    A["评估接口成本"] --> B["快接口重点看QPS"]
    B --> C["慢接口重点看并发"]
    C --> D["同时保护下游资源"]

关联流控解决什么

关联流控常用于“保护核心写接口”。比如订单系统中,查询订单可以多一些,提交订单是核心。如果提交订单压力很大,就可以限制查询刷新,给提交订单让资源。

text
当 submitOrder 资源压力超过阈值时,限制 queryOrder 资源。

这背后的思想是:系统资源有限,不能所有接口一视同仁。核心链路要优先保,非核心链路可以让路。

排队等待和快速失败

被限流时有两种常见处理:

处理方式含义适合场景
快速失败超过阈值立即拒绝秒杀、登录、查询、开放接口
匀速排队请求排队按固定速度通过消息发送、批量写入、削峰入库

匀速排队不是无限排队。队列等待时间过长,用户体验会很差,线程也可能被占住。对前端实时接口,通常更适合快速失败并提示稍后重试;对后台批处理,可以考虑排队或消息队列削峰。

限流、熔断、降级的工作差异

能力触发依据发生在什么时候返回什么
限流QPS、并发线程数、热点参数超过阈值请求进入时blockHandler 或统一限流响应
熔断慢调用比例、异常比例、异常数超过阈值一段统计窗口后快速拒绝,避免继续调用不健康依赖
降级限流/熔断/业务异常后的兜底请求被拒或业务失败后兜底结果

一句话:

text
限流挡住过量入口,熔断隔离不健康依赖,降级给调用方一个可接受的失败结果。

熔断器状态流转

熔断器通常有三个状态:

状态含义
CLOSED正常放行,并统计调用结果
OPEN熔断打开,直接拒绝请求
HALF_OPEN过一段时间后试探放行少量请求
mermaid
flowchart TD
    A["CLOSED正常统计"] --> B["指标超阈值"]
    B --> C["OPEN快速失败"]
    C --> D["到期进入HALF_OPEN"]
    D --> E["按探测结果转移状态"]

这能避免下游已经故障时,上游继续把请求打过去。它牺牲一段时间的可用结果,换取系统整体稳定。

熔断规则讲细:慢调用、异常比例、异常数

熔断解决的不是“流量太大”,而是“依赖不健康”。依赖可能是 Feign 下游服务、数据库、Redis、ES、外部医院接口,也可能是本服务内部的慢方法。

熔断类型判断方式适合场景
慢调用比例最近窗口内 RT 超过阈值的请求比例过高下游变慢、数据库慢、外部接口慢
异常比例最近窗口内异常请求比例过高下游持续 500、业务异常激增
异常数最近窗口内异常数量超过阈值低 QPS 但异常很明显的接口

慢调用比例为什么很重要

异常并不是唯一风险。很多雪崩不是从报错开始,而是从“变慢”开始。

mermaid
flowchart TD
    A["下游响应从 50ms 变成 3s"] --> B["上游线程等待时间变长"]
    B --> C["线程池逐渐被占满"]
    C --> D["新请求排队"]
    D --> E["更多请求超时和重试"]
    E --> F["系统雪崩"]

慢调用比例熔断可以在错误率大面积上升前先保护上游。比如:

text
RT 阈值: 1000ms
慢调用比例阈值: 50%
统计窗口: 10s
最小请求数: 20
熔断时长: 5s

含义是:最近 10 秒内至少有 20 次请求,并且超过 1000ms 的请求比例达到 50%,就打开熔断 5 秒。5 秒内后续请求快速失败,不再继续打下游。

最小请求数为什么必要

如果没有最小请求数,一个接口刚好 2 次请求,1 次慢,就达到 50% 慢调用比例,立刻熔断,这显然不合理。

最小请求数用于避免样本太少导致误判:

最小请求数风险
太小容易误熔断
太大低流量接口可能迟迟不触发保护

所以熔断规则要结合接口 QPS 设置。高 QPS 接口可以设置较高最小请求数;低 QPS 关键接口可以用异常数或较小窗口谨慎配置。

热点参数限流:为什么只保护某个参数

热点参数限流解决的是“总流量不高,但某个 key 特别热”。

典型场景:

场景热点参数
爆款商品详情skuId
某医院接口异常重试hospitalCode
某租户批量导出tenantId
某资产被频繁查询assetId

如果只配接口总 QPS,可能出现:

text
接口总 QPS = 500,没有超过阈值
skuId=1001 的 QPS = 450,打爆缓存和数据库
其他商品几乎没人访问

热点参数限流可以只限制 skuId=1001,不影响其他商品。

mermaid
flowchart TD
    A["请求进入商品详情"] --> B["提取 skuId"]
    B --> C["按参数值独立统计"]
    C --> D["只限制超过阈值的热点值"]

这也是为什么 Redis 热 key、ES 热查询、单医院接口重试,都不能只看接口总流量。

热点参数规则怎么工作

热点参数限流不是只看接口总次数,而是把“参数位置 + 参数值”当成更细的统计维度。例如商品详情接口第 0 个参数是 skuId,Sentinel 会按每个 skuId 分别统计访问频率。

mermaid
flowchart TD
    A["请求进入 getProduct skuId=1001"] --> B["ParamFlowSlot提取参数"]
    B --> C["定位参数索引 paramIdx=0"]
    C --> D["按 skuId=1001 单独统计QPS"]
    D --> E{"是否超过该参数阈值"}
    E -- "否" --> F["继续执行业务"]
    E -- "是" --> G["抛出ParamFlowException"]

这样做的原因是:很多线上热点不是“接口整体热”,而是“某个值特别热”。如果只限制接口总 QPS,要么阈值太低误伤所有正常商品,要么阈值太高保护不了爆款商品。

配置项含义容易踩坑
资源名被保护的方法或接口@SentinelResource 的 value 不一致会不生效
参数索引第几个参数作为热点 keyController、Service、Feign 参数顺序要确认
单机阈值每个参数值在单实例上的通过量多实例总量会乘以实例数
统计窗口多长时间内统计热点窗口太短抖动,太长恢复慢
例外项给指定参数值单独阈值爆款商品、大租户、重点医院可单独配置

热点参数限流不能替代 Redis 热 Key 治理。如果 skuId=1001 被限流后仍有大量请求命中缓存和数据库,还要配合本地缓存、请求合并、缓存预热、异步刷新、库存令牌和页面静态化。

热点参数商业示例:医院采集

医疗采集场景里,总采集 QPS 可能不高,但某一家医院接口异常慢,调度器和人工补采一直重试,最终拖垮全局采集线程池。这时可以把 hospitalCode 作为热点参数:

java
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;

public class HospitalCollectService {

    public String collect(String hospitalCode) {
        Entry entry = null;
        try {
            entry = SphU.entry("hospital-collect", 1, hospitalCode);
            return doCollect(hospitalCode);
        } catch (BlockException ex) {
            return "医院 " + hospitalCode + " 当前采集过载,已进入排队或补偿";
        } finally {
            if (entry != null) {
                entry.exit(1, hospitalCode);
            }
        }
    }

    private String doCollect(String hospitalCode) {
        return "collect " + hospitalCode;
    }
}

这个 Demo 表达的是核心原理:进入资源时带上热点参数,退出资源时也要带上相同参数数量。真实 Spring Cloud 项目通常用 @SentinelResource、Gateway Filter 或 Feign 适配器接入,不一定手写 SphU.entry()

Spring Cloud 接入示例

依赖示例:

xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

配置示例:

yaml
spring:
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8858
        port: 8719

接口降级示例:

java
@RestController
public class OrderController {
    @GetMapping("/orders/{id}")
    @SentinelResource(
        value = "getOrder",
        blockHandler = "handleBlocked",
        fallback = "handleFallback"
    )
    public String getOrder(@PathVariable Long id) {
        if (id < 0) {
            throw new IllegalArgumentException("invalid id");
        }
        return "order-" + id;
    }

    public String handleBlocked(Long id, BlockException ex) {
        return "请求太频繁,请稍后再试";
    }

    public String handleFallback(Long id, Throwable ex) {
        return "订单服务暂时不可用";
    }
}

区别:

  • blockHandler 处理 Sentinel 规则拦截,例如限流。
  • fallback 处理业务异常或降级兜底。

blockHandler 和 fallback 签名要求

blockHandler 方法要额外接收 BlockException,参数列表要能和原方法匹配。

java
public String handleBlocked(Long id, BlockException ex) {
    return "请求太频繁,请稍后再试";
}

fallback 方法通常接收 Throwable

java
public String handleFallback(Long id, Throwable ex) {
    return "订单服务暂时不可用";
}

如果方法签名不匹配,运行时可能找不到处理方法。生产项目里更推荐把降级方法放到单独类中,避免 Controller 变得臃肿。

商业场景:保护医疗采集链路

医疗采集平台常见保护点:

资源风险Sentinel 规则
/collect/start人工或系统重复触发采集按用户或医院限流
字典服务 Feign 调用下游慢导致采集线程堆积慢调用比例熔断
单医院数据拉取某医院接口异常重试过多热点参数限流,参数为 hospitalCode
资产检索接口高峰查询打爆 ESQPS 限流 + 降级
导出接口大导出占满线程并发线程数限流

示例:按医院编码做热点参数限流。

java
@SentinelResource(
    value = "collectByHospital",
    blockHandler = "collectBlocked"
)
public String collectByHospital(String hospitalCode) {
    return collector.collect(hospitalCode);
}

public String collectBlocked(String hospitalCode, BlockException ex) {
    return "医院 " + hospitalCode + " 当前采集请求过多,请稍后重试";
}

这样某一个医院接口异常时,不会把所有采集线程都占满。

限流和熔断的区别

mermaid
flowchart TD
    A["先判断问题来源"] --> B["过量流量使用限流"]
    B --> C["不健康依赖使用熔断"]
对比限流熔断
触发原因请求量超过阈值慢调用、异常比例、异常数超过阈值
保护对象当前服务和下游资源调用链路上的依赖
典型返回“请求过多”“服务暂时不可用”

阈值怎么设置

不要凭感觉写阈值。推荐步骤:

  1. 压测接口,得到单实例可承受 QPS、平均耗时、P95 耗时。
  2. 结合实例数量,计算总容量。
  3. 为核心接口预留安全余量。
  4. 先在测试环境验证规则。
  5. 上线后观察监控,再逐步调整。

例如单实例稳定承受 200 QPS,部署 3 个实例,总容量约 600 QPS。考虑安全余量,可以先把入口限流设为 450 到 500 QPS,再根据监控调整。

阈值设置的细化方法

QPS 限流

适合请求很短、线程占用不高的接口。设置时要看单实例容量。

text
单实例稳定 QPS = 压测得到的 P95 可接受情况下吞吐
集群总阈值 = 单实例稳定 QPS * 实例数 * 安全系数

安全系数通常不要取 1,可以先用 0.7 到 0.8。

并发线程数限流

适合慢接口、导出、外部接口调用。一个接口 QPS 不高,但每次执行 30 秒,也可能占满线程。

text
并发数 = QPS * 平均响应时间秒数

如果接口每秒 10 个请求,每个请求平均 5 秒,那么大约会占 50 个并发执行位置。

慢调用比例熔断

适合保护下游依赖。比如字典服务正常 50ms 返回,如果最近一段时间超过 1s 的请求比例很高,就说明下游不健康,应短暂熔断。

系统规则:为什么不是接口级限流

流控规则保护某个资源,系统规则保护整台应用实例。它关注的是“机器或进程整体是否快扛不住了”,例如 CPU、系统负载、入口 QPS、总线程数、平均 RT。

mermaid
flowchart TD
    A["请求进入任意入口资源"] --> B["读取系统实时指标"]
    B --> C{"CPU、Load、线程数或入口QPS是否超阈值"}
    C -- "否" --> D["继续资源级规则判断"]
    C -- "是" --> E["触发SystemBlockException"]

系统规则适合做最后一道保护线,但不要把它当成精确容量治理。原因是系统指标往往比较粗:

指标保护意图风险
Load机器运行队列和CPU压力容器环境下和宿主机指标可能有偏差
CPU usageCPU打满时拒绝新请求短抖动容易误伤,需要结合窗口观察
入口 QPS控制整个应用入口流量无法区分核心接口和非核心接口
线程数避免全局并发过高慢接口和快接口混在一起
平均 RT整体响应变慢时保护平均值可能掩盖P99尾延迟

商业项目里,系统规则通常作为兜底:

  1. Gateway 或入口层做租户、用户、接口级限流。
  2. 服务层对核心资源做 QPS、并发、热点参数和熔断。
  3. 系统规则在 CPU、Load 或总入口流量异常时兜底拒绝。
  4. 拒绝时返回明确错误码,不要伪装成业务成功。

如果只依赖系统规则,问题会很粗糙:一个低价值导出接口把 CPU 打满时,系统规则可能把下单接口也一起拦住。更好的做法是先把导出接口做并发限制、异步化和独立线程池,再让系统规则兜底。

集群流控:单机阈值和全局阈值的区别

单机限流只知道本实例流量。部署多个实例时,总通过量大约等于单机阈值乘以实例数。比如每台 200 QPS,5 台就是 1000 QPS。如果下游数据库只能承受 600 QPS,扩容应用后反而可能把数据库打爆。

mermaid
flowchart TD
    A["多个应用实例"] --> B["各自本地限流"]
    B --> C["总流量随实例数增加"]
    C --> D["共享下游数据库或第三方被打满"]
    A --> E["集群流控Token Server"]
    E --> F["按全局配额发放令牌"]

集群流控的目标是把“全局额度”集中裁决。各实例向 Token Server 请求令牌,拿到令牌才放行。它适合租户总额度、第三方接口总额度、数据库总写入额度这类不能随实例数线性放大的资源。

方案优点代价适合
单机限流简单、无中心依赖、延迟低总量随实例数变化,负载不均会误差单实例容量保护、普通接口
集群流控更接近全局配额Token Server 成为新依赖,需要超时和降级策略第三方额度、租户合同额度、共享数据库保护
Gateway 全局限流入口统一,便于按用户/租户绕过Gateway的内部调用保护不到外部API、统一入口
Redis Lua 限流跨实例共享状态,实现灵活Redis热点和网络依赖中低频全局额度、登录验证码

集群流控也不是银弹。Token Server 不可用时,要提前定义 Fail-open 还是 Fail-closed:

业务推荐倾向
普通查询可短时间 Fail-open,但启用本地保守阈值
短信、验证码、登录风控更偏 Fail-closed 或降级排队
支付、库存、扣费不能靠流控决定成功,必须回到业务状态机
第三方有硬额度接口优先 Fail-closed,避免超额被封禁

Warm Up 和匀速排队怎么选

限流效果也分不同流控行为。

行为原理适合不适合
快速失败超过阈值立即拒绝实时接口、秒杀、登录、查询必须全部处理的后台任务
Warm Up阈值从较低值逐步升高冷启动、缓存预热、JIT预热突发但必须立即吃满容量的系统
匀速排队按固定间隔放行请求消息发送、异步入库、削峰写入用户实时请求、长时间排队

Warm Up 的意义是防止新实例刚启动就吃满流量。新实例可能还没完成 JIT、连接池、缓存、线程池预热,直接接满流量会让 P99 飙升。滚动发布时,Warm Up、Nacos 权重、Readiness 和连接池预热应配合使用。

匀速排队适合把突发请求“摊平”,但必须有最大等待时间。否则请求虽然没有被拒绝,却一直占用线程,最后还是拖垮服务。实时接口一般宁可快速失败,也不要让用户等很久。

规则持久化

控制台临时配置的规则如果不持久化,应用重启后会丢失。生产环境要接入 Nacos、Apollo、ZooKeeper 或文件数据源。

mermaid
flowchart TD
    A["Sentinel Dashboard"] --> B["修改规则"]
    B --> C["推送到 Nacos"]
    C --> D["应用监听规则变化"]
    D --> E["本地生效"]
    E --> F["应用重启后重新从 Nacos 拉取"]

为什么控制台规则不能只存在内存

Sentinel Dashboard 默认更像“规则管理入口”,不是天然的强持久化配置中心。如果规则只存在应用内存或控制台临时推送,重启后可能丢失。

生产规则链路应该是:

mermaid
flowchart TD
    A["运维或开发修改规则"] --> B["规则写入 Nacos / Apollo"]
    B --> C["应用监听配置变化"]
    C --> D["Sentinel 本地规则管理器更新"]
    D --> E["新请求按新规则判断"]

规则应该纳入配置管理和发布流程,至少要做到:

要求原因
规则有持久化来源应用重启不丢规则
规则有环境隔离测试环境规则不能影响生产
规则有审批或变更记录防止误把限流阈值改太小
规则有回滚方案错误规则可以快速恢复
规则和监控联动规则是否生效要能观察

OpenFeign 接入 Sentinel 怎么理解

Feign 调用下游时,Sentinel 可以把 Feign 方法或目标服务当成资源进行保护。下游变慢或异常时,上游不应该无限等待和重试,而应该快速失败、降级或记录补偿。

mermaid
flowchart TD
    A["订单服务调用 StockClient.lock"] --> B["Feign 代理"]
    B --> C["Sentinel判断"]
    C --> D["通过后选择库存实例"]
    D --> E["HTTP客户端执行调用"]
    E --> F["记录RT与调用结果"]
    F --> G["阻断或失败按语义处理"]

配置示例:

yaml
feign:
  sentinel:
    enabled: true

Feign Client 示例:

java
@FeignClient(
        name = "stock-service",
        fallbackFactory = StockClientFallbackFactory.class
)
public interface StockClient {
    @PostMapping("/stocks/lock")
    Result<Void> lock(@RequestBody LockStockCommand command);
}

降级工厂:

java
@Component
public class StockClientFallbackFactory implements FallbackFactory<StockClient> {
    @Override
    public StockClient create(Throwable cause) {
        return command -> {
            log.warn("锁库存调用失败, requestNo={}, cause={}",
                    command.getRequestNo(), cause.toString());
            return Result.fail("库存服务繁忙,请稍后重试");
        };
    }
}

写操作要特别注意:fallback 不能返回成功。锁库存、支付、发货这类操作失败后,应该返回失败、进入待确认状态或记录补偿任务。

生产排查:Sentinel 拦截了请求怎么办

线上看到“接口被限流”或“走了降级”,不要只把阈值调大。先判断是哪类规则拦截。

mermaid
flowchart TD
    A["请求被Sentinel拦截"] --> B["识别具体异常类型"]
    B --> C["检查对应规则与实时指标"]
    C --> D["对照实例、版本和发布时间"]
    D --> E["判断误配置还是真实过载"]

排查证据:

证据说明
资源名哪个接口或方法被保护
规则类型流控、熔断、热点、系统、授权
当前 QPS 和通过 QPS是否超过阈值
block QPS每秒被拒多少
RT、P95、P99是否慢调用变多
异常比例和异常数是否下游错误激增
线程池和连接池是否等待资源
下游日志和 traceId是否真正打到下游

常见误判:

现象不要立刻做什么正确方向
被限流不要只把阈值调大先确认容量、热点 key、是否异常重试
被熔断不要只关熔断查下游慢 SQL、GC、线程池、网络
fallback 返回很多不要以为业务成功看 fallback 是否代表失败、是否需要补偿
block QPS 很高不要只怪 Sentinel可能入口流量、重试、爬虫、批任务异常

常见错误

错误后果建议
所有接口共用一个资源名规则互相影响每个关键接口使用清晰资源名
降级结果和正常结果格式不同前端处理混乱统一响应结构和错误码
只配置限流不做提示用户体验差返回明确“稍后重试”
阈值凭感觉要么无效,要么误伤根据压测和监控设置
规则没有持久化应用重启后规则丢失接入 Nacos、Apollo 等规则源

练习

  1. 给一个查询接口加 @SentinelResource
  2. 配置 QPS 为 1 的流控规则,连续刷新观察限流结果。
  3. 模拟下游接口慢调用,配置熔断规则。
  4. 把降级返回改成统一 JSON 结构。
  5. 思考哪些接口应该限流,哪些接口不应该被简单限流。

小结

Sentinel 的核心是流量治理。限流解决“流量太大”,熔断解决“依赖不健康”,热点参数保护解决“局部热点”。真正落地时,规则要来自压测和监控,降级返回要符合业务语义,规则还要持久化,否则重启后保护能力会丢失。