文章目录
- Sentinel简述
- [Sentinel 架构原理](#Sentinel 架构原理)
- [Sentinel 工作原理](#Sentinel 工作原理)
- [Sentinel 功能描述](#Sentinel 功能描述)
- 整合Sentinel-基础场景
- 异常处理
- 流控规则
- 流控效果
- 熔断规则
- [blockHandler 、fallback](#blockHandler 、fallback)
Sentinel简述
Sentinel 是什么?
Sentinel 是阿里巴巴开源的一款微服务流量治理组件。它主要用来保护微服务,防止某个接口因为访问量过大、响应太慢或者下游服务故障,最终拖垮整个系统。
简单来说:Sentinel 就像微服务系统中的"流量警察"和"安全保护器"。
Sentinel 主要解决什么问题?
在微服务系统中,一个请求通常会经过多个服务:
text
用户请求
↓
订单服务
↓
商品服务
↓
库存服务
↓
数据库
假设库存服务突然响应很慢,大量请求就会堆积在订单服务中。请求不断增加后,可能出现:
text
线程被占满
连接池被占满
CPU 使用率升高
内存持续增长
订单服务无法处理其他请求
最终整个调用链发生雪崩
Sentinel 会对这些请求进行监控和控制,在系统即将超出承受能力之前,主动限制一部分请求,避免故障继续扩大。
Sentinel 能做什么?
Sentinel 的核心功能主要包括:
-
流量控制,限制接口每秒可以接收的请求数量。
-
熔断降级,当下游服务响应太慢或者大量报错时,Sentinel 会暂时停止调用该服务。
-
系统保护,Sentinel 可以根据整个服务器的运行状态进行保护,例如:CPU 使用率、系统负载、线程数量、入口请求数量、接口响应时间。当系统整体压力过大时,Sentinel 会限制新的请求进入,让系统先处理已经接收的请求。
-
热点参数限流,Sentinel 不仅可以限制整个接口,还可以限制某个参数。例如商品查询接口:
text/product/1001 /product/1002 /product/1003正常情况下,不同商品访问量比较平均。但是商品 1001 突然成为爆款:/product/1001 每秒访问 5000 次,Sentinel 可以只限制商品 1001 的访问,不影响其他商品:
text/product/1001:限流 /product/1002:正常访问 /product/1003:正常访问这就是热点参数限流。
-
授权控制,Sentinel 可以根据请求来源决定是否允许访问。例如:
text订单服务:允许调用商品服务 后台管理服务:允许调用商品服务 测试服务:禁止调用商品服务它类似于简单的黑白名单控制。
Sentinel 中的几个核心概念
-
资源:资源就是 Sentinel 要保护的对象。资源可以是:
text一个 Controller 接口 一个 Service 方法 一个 OpenFeign 调用 一段业务代码例如:
java@GetMapping("/product/{id}") @SentinelResource(value = "getProduct") public Product getProduct(@PathVariable Long id) { return productService.getById(id); }这里的:getProduct。就是资源名称。
-
规则:规则表示如何保护这个资源。例如:
text资源名称:getProduct 限流阈值:100 统计方式:QPS表示:getProduct 接口每秒最多允许通过 100 个请求。Sentinel 常见规则包括:
text流控规则 熔断规则 热点规则 系统规则 授权规则 QPS
Sentinel 的核心目的就是: 在高流量或服务故障时,牺牲少部分请求,保护整个系统继续运行。
一句话总结: Sentinel 是一款微服务流量治理和容错保护组件,通过限流、熔断、降级、热点控制和系统保护,防止流量过大或下游服务故障导致整个微服务系统雪崩。
Nacos: 负责服务注册、发现和配置管理
OpenFeign: 负责微服务之间的远程调用
Sentinel: 负责限流、熔断、降级和系统保护
Sentinel 架构原理

-
业务应用引入 Sentinel Core,在每个微服务内部建立本地保护能力。
订单服务、商品服务、用户服务等业务应用引入 Sentinel 依赖后,每个微服务内部都会运行一套 Sentinel Core。 它会将 Controller 接口、Service 方法、OpenFeign 远程调用等需要保护的代码定义为 Sentinel 资源, 并在业务执行前完成规则检查。Sentinel Core 主要负责资源定义、请求统计、规则校验和异常处理。 需要注意,真正执行限流、熔断和降级判断的是各个微服务内部的 Sentinel Core,而不是 Sentinel Dashboard。即使多个服务使用同一个控制台,每个服务仍然独立统计自己的流量,并在本地执行自己的保护规则。
-
业务请求进入微服务后,由 Sentinel Core 统计数据并执行规则判断。
当请求进入订单服务、商品服务或用户服务时,会先经过 Sentinel 的资源入口。Sentinel Core 会实时统计该资源的 QPS、并发线程数、响应时间、慢调用数量、异常数量和异常比例等指标,然后根据本地加载的流控、熔断、热点参数、授权和系统保护规则进行判断。请求通过规则检查后,才会继续执行 Controller、Service、数据库查询或 OpenFeign 远程调用;如果没有通过规则检查,Sentinel 会立即阻止后续业务执行,并抛出 BlockException ,然后由 blockHandler、Fallback 或统一异常处理返回"系统繁忙,请稍后重试"等兜底结果。
-
Sentinel Dashboard 统一管理所有接入 Sentinel 的微服务。
Sentinel Dashboard 是 Sentinel 的可视化控制台, 主要负责规则管理、实时监控、机器发现和链路查看。订单服务、商品服务和用户服务启动并连接控制台后,会定期向 Dashboard 发送心跳, 告诉控制台自己的应用名称、IP、端口等信息,因此控制台可以发现当前有哪些 Sentinel 客户端在线。同时,各个微服务会把资源访问数据上报给 Dashboard,例如接口的 QPS、通过数量、阻塞数量、响应时间和异常情况,开发人员可以在控制台中查看每个服务、每个资源的实时运行状态以及调用链路。
-
开发人员在 Dashboard 中创建规则,再将规则推送到对应微服务。
开发人员可以在 Sentinel Dashboard 中为不同服务和资源创建流控规则、熔断规则、热点参数规则、授权规则以及系统保护规则。例如,为商品查询接口设置每秒最多通过 100 个请求;为订单服务调用商品服务设置慢调用比例熔断;或者限制只有指定来源的服务才能访问某个资源。规则创建后,Dashboard 会把规则推送给对应的微服务客户端,Sentinel Core 收到规则后加载到应用本地。后续请求到达时,就会立即按照新规则进行判断,不需要重启整个微服务。
-
动态规则数据源负责持久化规则,并同步到所有服务实例。
如果规则只保存在 Sentinel Dashboard 或微服务内存中,当应用重启或 Dashboard 重启后,规则可能丢失。因此,生产环境通常会接入 Nacos、Apollo、ZooKeeper或本地文件等动态规则数据源, 将 Sentinel 规则持久化保存。以 Nacos 为例,开发人员修改规则后,可以把规则写入 Nacos;各个微服务监听 Nacos 中的规则配置,规则变化后自动刷新本地规则。这样,即使订单服务部署了多个实例,每个实例也可以从同一个规则数据源读取相同规则,保证集群中的规则一致。Dashboard 主要负责可视化管理,动态规则数据源负责规则的长期保存和同步。
-
多个微服务实例可以按需使用集群流控。
默认情况下,每个微服务实例都在本地独立统计流量。例如,订单服务部署了三台实例,每台实例配置 QPS 阈值为 100,那么三台实例合计可能允许约 300 QPS。 某些场景要求整个集群共享一个总阈值,此时可以使用 Sentinel 集群流控。集群中的 Token Client 在请求进入时向 Token Server 申请令牌,Token Server 统一统计整个集群的流量,并决定是否发放令牌。申请成功,请求继续执行;申请失败,请求被限流。通过这种方式,可以把"每台机器各自限流"升级为"整个服务集群统一限流"。集群流控属于可选能力,普通项目使用 Sentinel Core 的本地限流即可。
-
Sentinel 各部分共同形成"本地执行、控制台管理、数据源持久化"的架构。
Sentinel Core 嵌入每个业务应用,在请求路径中完成资源统计、规则判断、限流、熔断和异常处理;Sentinel Dashboard 统一发现机器、展示监控数据、查看调用链路并管理规则;Nacos 等动态规则数据源负责持久化和同步规则;集群流控则在需要统一控制整个集群流量时,由 Token Server 和 Token Client 统一分配令牌。Dashboard 不会转发业务请求,也不会代替微服务执行规则,因此业务请求仍然直接进入对应服务,保护判断也在应用本地完成。
总结: Sentinel 采用"客户端嵌入式治理 + 控制台统一管理 + 动态数据源持久化"的架构。各个微服务内部的 Sentinel Core 负责实时统计并执行限流、熔断等规则;Dashboard 负责监控、机器发现和规则配置;Nacos 等数据源负责规则持久化与动态同步;需要对整个集群统一限制流量时,还可以使用 Token Server 和 Token Client 实现集群流控。
一句话理解: Sentinel Core 负责在微服务本地真正执行保护规则,Dashboard 负责统一查看和管理规则,Nacos 等数据源负责保存和同步规则。
Sentinel 工作原理

Sentinel 是一个面向分布式系统的流量控制和服务容错组件。它会把需要保护的接口、方法或远程调用定义为"资源",在业务代码真正执行之前,先统计资源的访问情况,再根据流控、熔断、系统保护、授权等规则决定本次请求是继续执行,还是直接拦截并返回兜底结果。
- 系统启动时加载规则并定义受保护资源。
Spring Boot 应用启动后,会初始化 Sentinel 客户端,并加载流控、熔断、系统保护、热点参数和授权等规则。Controller 接口、Service 方法、OpenFeign 调用以及通过 @SentinelResource 标记的方法,都可以被定义为 Sentinel 资源。规则和资源信息最终保存在应用本地,由微服务中的 Sentinel 客户端负责执行。 - 业务请求进入 Sentinel 资源入口。
当请求访问受保护的接口或方法时,Sentinel 会为本次调用创建资源入口 Entry,并根据资源名称查找对应的保护规则。请求不会立即执行后面的业务代码,而是先进入 Sentinel 的规则检查流程。 - 请求经过 ProcessorSlot 责任链检查。
请求进入资源后,会依次经过 Sentinel 内部的 ProcessorSlot 责任链。StatisticSlot 负责统计 QPS、并发线程数、响应时间和异常数量;AuthoritySlot 检查访问来源;SystemSlot 检查系统整体负载;FlowSlot 执行流量控制;DegradeSlot 执行熔断判断;ParamFlowSlot 检查热点参数。每个 Slot 完成自己的工作后,再将请求交给下一个 Slot。 - Sentinel 根据实时数据判断是否放行。
Sentinel 会结合当前资源的实时运行数据和已加载的规则,判断请求是否超过流量阈值、系统是否过载、资源是否处于熔断状态、请求来源是否允许以及热点参数是否访问过于频繁。只有所有规则都检查通过,请求才可以继续执行。 - 通过检查则执行正常业务,未通过则进入限流或降级处理。
请求通过规则检查后,会继续执行 Controller、Service、数据库操作或 OpenFeign 远程调用。请求未通过检查时,Sentinel 会立即阻止后续业务执行并抛出 BlockException,然后由 blockHandler、Fallback 或统一异常处理返回限流提示或兜底结果。BlockException 表示请求被 Sentinel 规则拦截,与业务代码自身产生的普通异常不同。 - Sentinel 持续统计指标,并由 Dashboard 统一监控和管理规则。
请求执行结束后,Sentinel 会记录通过数量、阻塞数量、QPS、并发线程数、响应时间和异常比例等指标,这些数据会继续参与后续的流控和熔断判断。微服务还会将监控数据上报给 Sentinel Dashboard,开发人员可以在控制台中查看运行情况并配置规则。Dashboard 负责监控和规则管理,真正的规则判断、限流和熔断仍然在各个微服务本地完成。
总结: Sentinel 的完整工作过程就是:系统启动时加载保护规则;业务请求到达后先进入 Sentinel 资源入口;请求经过 ProcessorSlot 责任链完成数据统计和规则检查;通过检查则放行业务请求并正常返回结果;未通过检查则抛出 BlockException,执行限流提示或兜底逻辑;同时 Sentinel 持续记录 QPS、响应时间和异常比例等指标,并将监控数据上报到 Dashboard。
一句话理解:
请求进入 Sentinel 后,会先进行实时统计,再依次检查授权、系统保护、流控、熔断和热点参数等规则;检查通过才执行真正的业务,检查不通过则立即拦截,并返回限流或降级结果。
Sentinel 功能描述
随着微服务架构越来越复杂,服务之间的调用关系也越来越多。当某个服务流量突然增加、响应速度变慢或者发生大量异常时,故障可能沿着调用链不断传播,最终导致多个服务同时不可用。Sentinel 以流量为切入点,从流量控制、熔断降级、系统保护、热点参数保护等多个维度保护微服务的稳定性。
- 流量控制, 限制单位时间内进入系统的请求数量。
当某个接口的访问量超过系统能够承受的范围时,Sentinel 可以根据配置的 QPS、并发线程数等阈值对请求进行限制。例如,商品查询接口最多只能承受每秒 100 个请求,那么第 101 个及之后的请求就可以被 Sentinel 拦截,避免大量请求同时进入业务代码、数据库或下游服务。被拦截的请求可以返回"系统繁忙,请稍后重试"等友好提示,从而保证已经进入系统的请求能够正常处理。Sentinel 还支持直接拒绝、排队等待、预热等流控效果,可以根据不同业务场景决定流量应该立即拒绝、匀速通过,还是逐渐增加。
- 根据调用关系和访问来源进行精细化流量控制。
Sentinel 不仅可以限制某个接口的总流量,还可以结合调用链路、请求来源和关联资源进行控制。例如,同一个商品查询接口可能同时被订单服务、购物车服务和后台管理系统调用,可以根据调用来源决定哪些服务允许访问、哪些服务限制访问。对于调用链路中的不同入口,也可以设置不同规则;即使最终调用的是同一个方法,也能够根据请求从哪个入口进入进行区分。关联限流则可以在某个关键资源压力升高时,限制与它相关的其他资源,优先保证核心业务正常运行。 - 通过线程数隔离、慢调用降级和异常熔断阻止故障扩散。
当下游服务响应变慢时,上游请求会长时间占用线程。如果请求不断积压,可能耗尽当前服务的线程池,导致原本正常的接口也无法访问。Sentinel 可以限制资源的并发线程数量,避免一个慢接口占满所有业务线程。同时,Sentinel 会统计接口的响应时间、慢调用比例、异常比例和异常数量。当慢调用或异常达到设定阈值时,Sentinel 会打开熔断器,在一段时间内暂时阻止请求继续调用故障服务,直接返回降级结果。熔断时间结束后,Sentinel 会尝试放行少量请求进行探测;如果服务恢复正常,就关闭熔断器,否则继续保持熔断。 - 通过系统自适应保护,防止整个应用被过载流量压垮。
普通流控主要保护某一个接口,而系统自适应保护关注的是整个应用的运行状态。Sentinel 可以根据系统负载、CPU 使用率、入口 QPS、并发线程数、平均响应时间等指标判断当前应用是否已经接近承载极限。当系统整体压力过高时,Sentinel 会限制新的流量继续进入,为正在执行的请求保留足够资源,避免系统因为 CPU 过高、线程堆积或者响应时间持续增长而彻底失去响应。它保护的不是单个业务接口,而是整个应用的可用性。 - 通过热点限流、 集群限流和削峰填谷处理特殊流量场景。
热点限流用于保护访问特别频繁的参数值。例如,商品查询接口整体流量并不高,但某个热门商品的 ID 被大量访问,Sentinel 可以只限制这个热点商品,而不影响其他普通商品的查询。集群限流用于多个服务实例共享同一个总流量阈值,例如订单服务部署了三台机器,需要整个集群每秒最多处理 1000 个请求,而不是每台机器各处理 1000 个请求。削峰填谷则可以将短时间内突然到达的大量请求排队并匀速放行,把瞬时流量高峰转换成相对平稳的请求流量,减少数据库和下游服务受到的冲击。 - Sentinel 控制台统一完成实时监控、机器发现和规则配置。
接入 Sentinel 的 Spring Cloud、Dubbo、gRPC 或 Service Mesh 应用会向 Sentinel Dashboard 上报心跳和运行指标。控制台可以发现在线应用和服务实例,并展示资源的 QPS、通过数量、限流数量、异常数量、响应时间等实时数据。开发人员还可以在控制台中配置流控、熔断、热点参数、授权和系统保护规则。需要注意,Dashboard 主要负责查看监控和管理规则,真正的规则判断和请求拦截仍然由每个业务应用内部的 Sentinel Core 完成。 - 通过动态数据源实现规则持久化和自动同步。
如果规则只保存在 Sentinel Dashboard 或应用内存中,服务重启后规则可能丢失。因此,实际项目中通常会把规则保存到 Nacos、Apollo、ZooKeeper 等动态规则数据源中。微服务启动时从数据源读取规则,并持续监听规则变化;管理员修改规则后,新的规则会自动同步到各个服务实例,无需重启应用。这样既可以保证规则长期保存,也能保证同一个服务的多个实例使用统一规则。
总结: Sentinel 的主要功能就是在请求进入业务系统后,对流量、并发线程、响应时间、异常情况、热点参数、系统负载和请求来源进行实时统计与判断。请求符合规则就正常放行;请求超过限制、下游服务异常或系统已经过载时,就执行限流、熔断或降级,返回兜底结果,防止局部故障进一步扩散。
整合Sentinel-基础场景

下载的jar包,直接:java -jar sentinel-dashboard-1.8.8.jar 启动

在需要的模块中添加Sentinel依赖和配置配置:
xml
<!-- Sentinel:限流、熔断降级以及OpenFeign兜底支持 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
yml
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 # Sentinel Dashboard 控制台地址
eager: true # 项目启动时立即初始化 Sentinel,并向控制台发送心跳
访问: http://localhost:8080进入Sentinel Dashboard。默认端口是8080,用户名、密码默认都是:sentinel。


| 菜单 | 作用 |
|---|---|
| 实时监控 | 查看资源实时请求情况,例如:、通过QPS、拒绝QPS、平均响应时间 |
| 簇点链路 | 作用是:查看资源、查看调用链、直接为资源创建规则。注意:只有访问过的资源才会出现在簇点链路中 |
| 流控规则 | 集中查看、修改和删除所有流控规则。在簇点链路点击:+流控 创建完成后,就会出现在这里。 |
| 熔断规则 | 集中管理熔断降级规则。 |
| 热点规则 | 集中管理热点参数限流规则。 |
| 系统规则 | 从整个应用层面保护系统,而不是只保护某个接口。可以根据这些指标进行保护:、系统负载、CPU使用率、平均响应时间、入口QPS、最大并发线程数、 |
| 授权规则 | 管理黑名单、白名单规则。 |
| 集群流控 | 用于多个微服务实例之间统一计算流量。普通单机流控是:每个实例分别计算自己的QPS。集群流控是:多个实例共享一个总阈值 |
| 机器列表 | 查看当前应用连接到 Sentinel Dashboard 的所有实例,例如:、IP地址、Sentinel端口、心跳时间、健康状态、版本信息 |
操作按钮:就是为该资源添加不用的规则,+流控规则、 +熔断规则、 +热点规则、 +授权规则。
下面测试一下流控规则:


测试:快速点击 http://localhost:8000/order/normal/1 这个接口,让接口每秒超过1次请求。

异常处理
sentinel的异常类是BlockException,有多个子类,不同的异常子类对应不同规则,结构如下:
text
BlockException (被流控/熔断/降级的父类)
├─ FlowException // 【流控异常】触发了 QPS 或线程数限流规则
├─ ParamFlowException // 【热点参数异常】触发了热点参数限流规则
├─ DegradeException // 【熔断降级异常】(旧版) 触发了平均RT、异常比例或异常数降级规则
├─ SystemBlockException // 【系统保护异常】触发了系统自适应限流(如 Load 过高、CPU 使用率过高、入口 QPS 过高等)
├─ AuthorityException // 【授权异常】触发了黑白名单规则(来源访问权限被拒绝)
└─ CircuitBreakerException // 【断路器异常】(新版本 1.8.0+) 触发了新版熔断规则(慢调用比例或异常比例)
web接口异常处理
对于web接口,我们给用户返回默认的异常处理肯定是不有好的:

因此我们可以自定义一下,返回友好的信息:
java
package com.order.exception;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
httpServletResponse.setContentType("application/json;charset=UTF-8");
PrintWriter writer = httpServletResponse.getWriter();
HashMap<String, Object> result = new HashMap<>();
String resourceName = httpServletRequest.getRequestURI();
String message;
if (e instanceof FlowException) {
message = "请求过于频繁被限流,请稍后重试"; // 触发流量控制
} else if (e instanceof DegradeException) {
message = "服务暂时不可用,请稍后重试"; // 触发熔断降级
} else if (e instanceof ParamFlowException) {
message = "热点参数访问过于频繁"; // 触发热点参数限流
} else if (e instanceof AuthorityException) {
message = "当前请求没有访问权限"; // 触发授权规则
} else if (e instanceof SystemBlockException) {
message = "系统负载过高,请稍后重试"; // 触发系统保护
} else {
message = "请求被 Sentinel 拦截";
}
result.put("code", 10086);
result.put("msg", message);
result.put("resourceName", resourceName);
String json = objectMapper.writeValueAsString(result);
writer.write(json);
writer.flush();
writer.close();
}
}

@SentinelResource异常处理
使用@SentinelResource注解指定一个资源,当该资源出现异常应该怎么处理?
java
/**
* 正常远程调用。
*/
@SentinelResource(value = "getNormalOrder", blockHandler = "getNormalOrderFallback")
@Override
public OrderInfo getNormalOrder(Long productId) {
// 表面是Java方法调用,实际由Feign代理对象转换成HTTP请求
Product product = productFeignClient.getProductById(productId);
return buildOrder(productId, product);
}
// 兜底回调
public OrderInfo getNormalOrderFallback(Long productId, BlockException e) {
OrderInfo orderInfo = new OrderInfo();
orderInfo.setOrderId(0L);
orderInfo.setOrderName("未知用户订单,异常信息:" + e.toString());
return orderInfo;
}
@SentinelResource(value = "资源名称", blockHandler = "兜底方法"),如果出发规则则执行兜底方法。
按照上面web接口的测试方法,设置流控规则,每秒钟qps为1个。结果如下:

openfeign的异常
java
@FeignClient(
name = "cloud-product", // 根据服务名称从Nacos获取商品服务实例
configuration = OpenFeignConfig.class, // 加载重试器和请求拦截器
fallbackFactory = ProductFeignFallbackFactory.class // 最终调用失败时执行兜底类,可以获得具体失败原因
)
public interface ProductFeignClient {
/**
* 正常调用:
* 用于测试远程调用、负载均衡、日志和拦截器。
*/
@GetMapping("/product/{id}")
Product getProductById(@PathVariable("id") Long id);
}
=====================================================================================================
package com.order.feign.fallback;
import com.order.domain.Product;
import com.order.feign.ProductFeignClient;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* 商品服务OpenFeign兜底工厂。
*
* 当商品服务发生连接超时、读取超时、服务不可用、
* HTTP异常、Sentinel限流或Sentinel熔断时,创建兜底实现对象。
*
* 相比普通fallback,fallbackFactory可以获得具体的异常原因。
*/
@Component
public class ProductFeignFallbackFactory
implements FallbackFactory<ProductFeignClient> {
@Override
public ProductFeignClient create(Throwable cause) {
return new ProductFeignClient() {
@Override
public Product getProductById(Long id) {
// return createFallbackProduct(id, "商品服务调用失败:" + cause.getMessage());
return createFallbackProduct(id, "商品服务调用失败:" + getErrorMessage(cause));
}
@Override
public Product getSlowProductById(Long id) {
// return createFallbackProduct(id, "商品服务调用超时:" + cause.getMessage());
return createFallbackProduct(id, "商品服务调用超时:" + getErrorMessage(cause));
}
@Override
public Product getErrorProductById(Long id) {
// return createFallbackProduct(id, "商品服务发生异常:" + cause.getMessage());
return createFallbackProduct(id, "商品服务发生异常:" + getErrorMessage(cause));
}
};
}
/**
* 创建统一的商品兜底对象。
*/
private Product createFallbackProduct(
Long id,
String message) {
Product product = new Product();
product.setId(id);
product.setProductName("默认商品");
product.setPrice(null);
product.setServerPort("fallback");
product.setSourceService("cloud-order");
product.setInternalToken(null);
product.setFallback(true);
product.setMessage(message);
return product;
}
/**
* 获取简化后的异常信息。
*/
private String getErrorMessage(Throwable cause) {
if (cause == null) {
return "未知异常";
}
if (cause.getMessage() == null) {
return cause.getClass().getSimpleName();
}
return cause.getMessage();
}
}
SphU硬编码方式处理异常
SphU包含了 try-catch 风格的 API。用这种方式,当资源发生了限流之后会抛出 。这个时候可以捕捉异常,进行限流之后的逻辑处理。示例代码如下:BlockException
java
// 1.5.0 版本开始可以利用 try-with-resources 特性(使用有限制)
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try (Entry entry = SphU.entry("resourceName")) {
// 被保护的业务逻辑
// do something here...
} catch (BlockException ex) {
// 资源访问阻止,被限流或被降级
// 在此处进行相应的处理操作
}
注意,若 entry 的时候传入了热点参数,那么 exit 的时候也一定要带上对应的参数(exit(count, args)),否则可能会有统计错误。这个时候不能使用 try-with-resources 的方式。另外通过 Tracer.trace(ex) 来统计异常信息时,由于 try-with-resources 语法中 catch 调用顺序的问题,会导致无法正确统计异常数,因此统计异常信息时也不能在 try-with-resources 的 catch 块中调用 Tracer.trace(ex)。
手动 exit 示例:
java
Entry entry = null;
// 务必保证 finally 会被执行
try {
// 资源名可使用任意有业务语义的字符串,注意数目不能太多(超过 1K),超出几千请作为参数传入而不要直接作为资源名
// EntryType 代表流量类型(inbound/outbound),其中系统规则只对 IN 类型的埋点生效
entry = SphU.entry("自定义资源名");
// 被保护的业务逻辑
// do something...
} catch (BlockException ex) {
// 资源访问阻止,被限流或被降级
// 进行相应的处理操作
} catch (Exception ex) {
// 若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(ex, entry);
} finally {
// 务必保证 exit,务必保证每个 entry 与 exit 配对
if (entry != null) {
entry.exit();
}
}
热点参数埋点示例:
java
Entry entry = null;
try {
// 若需要配置例外项,则传入的参数只支持基本类型。
// EntryType 代表流量类型,其中系统规则只对 IN 类型的埋点生效
// count 大多数情况都填 1,代表统计为一次调用。
entry = SphU.entry(resourceName, EntryType.IN, 1, paramA, paramB);
// Your logic here.
} catch (BlockException ex) {
// Handle request rejection.
} finally {
// 注意:exit 的时候也一定要带上对应的参数,否则可能会有统计错误。
if (entry != null) {
entry.exit(1, paramA, paramB);
}
}
SphU.entry() 的参数描述:
| 参数名 | 类型 | 解释 | 默认值 |
|---|---|---|---|
| entryType | EntryType | 资源调用的流量类型,是入口流量()还是出口流量(),注意系统规则只对 IN 生效 EntryType.IN EntryType.OUT |
EntryType.OUT |
| count | int | 本次资源调用请求的 token 数目 | 1 |
| args | Object\[\] | 传入的参数,用于热点参数限流 | 无 |
注意: 需要与 方法成对出现,匹配调用,否则会导致调用链记录异常,抛出 异常。常见的错误:SphU.entry(xxx) entry.exit() ErrorEntryFreeException
- 自定义埋点只调用 ,没有调用 SphU.entry() entry.exit()
- 顺序错误,比如:,应该为 entry1 → entry2 → exit1 → exit2 entry1 → entry2 → exit2 → exit1
流控规则
只有请求过得接口,才会在簇点链路中显现出来
阈值类型

QPS
限流单位是"每秒允许通过的请求数量"。它是衡量系统处理能力最直接的指标。
如果你设置了 QPS 阈值为 10。那么在这个接口上,1秒钟内最多允许进来 10 个请求。如果在短短一秒内来了第 11 个请求,这个请求就会被 Sentinel 立即拦截(抛出 FlowException),返回被限流的提示。
适用场景:绝大多数互联网高并发业务。例如秒杀抢购、商品详情页查询、下单接口等。它用来防止突发的流量洪峰把数据库或服务器冲垮。
QPS底层原理
Sentinel 不是简单地在每个整秒把数字清零,而是使用了滑动时间窗口。
text
例如当前时间是:12:00:00.700
Sentinel统计的可能是:11:59:59.700 ~ 12:00:00.700
↓
过了 100 毫秒,当前时间变成:12:00:00.800
统计范围也向前移动:11:59:59.800 ~ 12:00:00.800
因此它叫:滑动时间窗口
可以把最近一秒切成几个小格子:最近1秒:
text
┌────────┬────────┬────────┬────────┐
│ 250ms │ 250ms │ 250ms │ 250ms │
└────────┴────────┴────────┴────────┘
1次 0次 1次 0次
Sentinel把这些小时间段中的请求数量加起来:1 + 0 + 1 + 0 = 2。得到最近一秒的通过数量。
随着时间前进,过期的小格子会被丢掉,新格子会加入:
text
旧窗口:
[A][B][C][D]
时间继续向前:
[B][C][D][E]
这样统计会比"每到整秒清零"更准确。
并发线程数
限流单位是"当前正在处理的线程数量"。它关注的是系统同时能支撑多少个任务并行处理,不关心任务执行得快还是慢。
如果你设置了并发线程数阈值为 20。
- 如果这个接口处理起来特别慢(比如需要 5 秒才能返回数据库结果),只要当前有 20 个线程正在处理这个接口请求,第 21 个进来的请求就会立刻被拦截(抛出 FlowException),而不是排队等待。
适用场景:耗时的、阻塞性强的接口。例如复杂的报表计算、大量数据的导出、依赖外部第三方 API 且响应慢的接口。
并发线程数的底层原理
假设配置:
text
阈值类型:并发线程数
单机阈值:2
意思是:当前最多允许 2 个请求同时执行这个资源。
Sentinel会为这个资源维护一个类似的数字:
text
当前正在执行的请求数:0
注意,这里不是统计整个 JVM 的线程总数,也不是 Tomcat 的全部线程数。
它统计的是:当前正在执行 /order/normal/{productId} 这个 Sentinel 资源的请求数量。
流控模式-直接
直接是最普通、最常用的模式。它的意思是:当前资源超过自己的阈值,就限制当前资源。 当前资源是:/order/normal/{productId}

Sentinel 检查:/order/normal/{productId}最近一秒通过了多少个请求,允许一秒钟有两个请求。执行过程:
text
第1个请求 → 当前资源QPS未超过2 → 放行
第2个请求 → 当前资源QPS未超过2 → 放行
第3个请求 → 当前资源QPS达到2 → 限流
流控模式-关联
关联模式的意思是:当前资源是否被限流,不看自己的流量,而是看另一个关联资源的流量。 假设有两个资源:
text
资源A:查询订单
资源B:修改订单
它们都要访问同一张订单表。现在希望:修改订单非常繁忙时,暂时限制查询订单,给修改业务腾出数据库资源。 可以这样设置:

最容易弄错的一点是:
text
被限制的是:资源A
检查依据是:资源B
这条规则不会直接限制 B,而是用 B 的流量决定是否限制 A。
流控模式-链路
使用链路模式,必须在配置文件添加下面配置:
yml
spring:
cloud:
sentinel:
web-context-unify: false # 关闭 Sentinel 对 Web 上下文(Context)的默认整合。配置目的:让"链路"流控模式生效
Sentinel 默认会收敛所有 URL 的入口 Context,将它们统一为同一个入口(例如 sentinel_web_servlet_context)。这意味着,所有通过 Web 请求产生的调用,在 Sentinel 的调用链树中都属于同一个父节点。在这种"整合"状态下,链路模式(只统计来自特定入口的请求)的流控规则无法生效,因为系统无法区分不同的调用来源。
将 web-context-unify 设置为 false 后,Sentinel 会根据不同的 URL 路径来创建不同的入口 Context。这样一来,调用链路树就能清晰地分出不同的入口,链路模式的流控规则就可以正常工作了。
链路模式用于解决:同一个资源被多个入口调用,只想限制其中一条调用链。
例如有一个公共 Service 方法:
java
@SentinelResource("queryProduct")
public Product queryProduct(Long id) {
return productMapper.selectById(id);
}
A、B两个 Controller 都调用它,同时希望queryProduct的QPS每秒钟超过两个时A限流,B不限流:
text
A B
│ │
↓ ↓
queryProduct 达到阈值限流A,B正常访问

链路模式不是简单限制整个 queryProduct,而是限制:某个入口 → queryProduct这一条具体调用路径。
三个模式可以这样记:
- 直接: 看自己,限制自己
- 关联: 看别人,限制自己
- 链路: 只限制从某个入口过来的自己
流控效果
| 流控模式 | 快速失败 | Warm Up | 排队等待 |
|---|---|---|---|
| 直接 | ✅ 支持 | ✅ 支持,但必须是 QPS | ✅ 支持,但必须是 QPS |
| 关联 | ✅ 支持 | ❌ 不支持 | ❌ 不支持 |
| 链路 | ✅ 支持 | ❌ 不支持 | ❌ 不支持 |
流控效果-快速失败

快速失败是默认方式
假设:QPS阈值 = 2。一秒内来了五个请求:
text
第1个 → 通过
第2个 → 通过
第3个 → 立即拒绝
第4个 → 立即拒绝
第5个 → 立即拒绝
被拒绝的请求:、不会等待、不会进入Controller、不会执行OpenFeign调用,而是直接返回兜底方法:
text
抛出FlowException
↓
进入MyBlockExceptionHandler
↓
返回自定义JSON
流控效果-Warm Up

Warm Up 是预热、冷启动模式。
它解决的问题是:系统长时间没有流量,突然一下进来大量请求,系统可能还没有准备好。
例如系统刚启动时:
text
缓存还没有加载
数据库连接池还没有充分建立连接
JVM代码还没有充分预热
各种对象还没有初始化完成
假设最终阈值是:10 QPS,快速失败模式下,相当于系统马上允许接收最高约 10 QPS。Warm Up 则是:
text
刚开始:只允许较少请求通过
↓
随着预热时间推进
↓
允许通过的QPS逐渐增加
↓
最终达到10 QPS
例如预热时间设置为 10 秒,可以大致理解成:
text
刚启动 → 只放少量请求
运行几秒后 → 放行能力逐渐增加
10秒之后 → 达到配置的完整阈值
它不是说 10 秒内完全不接收请求,而是:先用较低速度接收请求,再逐渐提高到完整阈值。
流控效果-排队等待

排队等待不会马上拒绝所有超额请求,而是:让请求按照比较均匀的速度依次通过。
假设设置:QPS阈值 = 2,意味着平均放行间隔大约为:1000毫秒 ÷ 2 = 500毫秒
大量请求同时到达:
text
请求A ── 立即通过
请求B ── 等待约500ms后通过
请求C ── 再等待约500ms后通过
请求D ── 继续等待
原本的突发流量,同一时刻:A B C D E 全部到达,经过排队等待后变成:
text
0ms → A通过
500ms → B通过
1000ms → C通过
1500ms → D通过
这就是流量整形,把不均匀的流量变得比较平稳。但排队不是无限等待,还需要设置:超时时间 例如最大排队时间是:1000ms,某个请求计算后需要等待 1500ms,超过最大等待时间,就会被拒绝:
text
预计等待1500ms
↓
最大只允许等待1000ms
↓
抛出FlowException
Sentinel 的排队等待采用匀速放行方式,官方文档将其描述为漏桶式流量整形,用于把突发请求按照稳定速率处理。
熔断规则
熔断规则是 Sentinel 用来监控资源运行状态,并在资源出现大量慢调用或异常时,暂时阻止请求继续访问该资源的一种保护规则。
它主要用于防止故障服务持续消耗线程、连接、CPU、内存等系统资源,避免局部故障沿着微服务调用链不断扩散,最终导致系统雪崩。
Sentinel 支持的熔断策略
- 慢调用比例
根据请求响应时间判断是否属于慢调用。当统计窗口内的慢调用比例达到设定阈值,并且请求数量达到最小请求数时,触发熔断。 - 异常比例
根据异常请求占总请求数的比例判断。当统计窗口内的异常比例达到设定阈值,并且请求数量达到最小请求数时,触发熔断。 - 异常数
根据统计窗口内出现的异常总数判断。当异常数量达到设定阈值,并且请求数量满足条件时,触发熔断。
断路器原理

断路器内部维护三种状态
text
CLOSED 关闭状态
OPEN 打开状态
HALF_OPEN 半开状态
状态变化完整流程:
请求正常通过
↓
┌─────────────────┐
│ CLOSED │
│ 正常统计状态 │
└────────┬────────┘
│
慢调用或异常达到阈值
│
↓
┌─────────────────┐
│ OPEN │
│ 请求直接拒绝 │
└────────┬────────┘
│
熔断时长结束
│
↓
┌─────────────────┐
│ HALF_OPEN │
│ 放行一个探测请求 │
└──────┬──────┬───┘
│ │
探测成功 探测失败
│ │
↓ ↓
CLOSED OPEN
- 正常状态下,断路器处于关闭状态 CLOSED。
请求可以正常进入业务方法或调用下游服务。Sentinel 会持续统计资源的响应时间、慢调用比例、异常比例和异常数量,用于判断服务是否稳定。 - 达到熔断阈值后,断路器切换为打开状态 OPEN。
当统计时间窗口内的请求数量达到要求,并且慢调用比例、异常比例或异常数量超过设定阈值时,Sentinel 会打开断路器,暂时切断对该资源的调用。 - 断路器打开后,请求快速失败并返回兜底结果。
在熔断时间内,后续请求不会再执行原来的业务逻辑,也不会继续调用故障服务,而是直接被 Sentinel 拦截,再通过 Fallback 或异常处理返回友好提示。 - 熔断时间结束后,断路器进入半开状态 HALF-OPEN。
Sentinel 会放行少量探测请求,用于判断服务是否已经恢复。此时不会立即恢复全部流量,避免尚未恢复的服务再次被大量请求压垮。 - 根据探测结果决定恢复还是继续熔断。
如果探测请求成功,断路器重新切换为 CLOSED,恢复正常调用;如果探测请求仍然异常或响应过慢,断路器重新进入 OPEN,继续等待下一次探测。
定义慢调用比例规则

熔断策略:慢调用比例(当请求的响应时间超过"最大 RT"阈值时,视为慢调用)
- 最大 RT:800 ms(响应时间超过 800ms 即被计为慢调用)
- 熔断时长:30 s(熔断开启后,拒绝请求的时间为 30 秒)
- 统计时长:5000 ms(统计窗口期为 5 秒,在此时间内采集指标)
- 比例阈值:0.5(即 50%)(当慢调用比例达到 50% 时触发熔断)
- 最小请求数:10(统计时长内请求数至少达到 10 次,才会计算比例并触发熔断)
统计 5 秒钟内,如果请求数达到 10 个,且其中有 50%(即 5 个) 的请求响应时间超过 800 ms,则触发熔断。
熔断开启后,后续请求将被直接拒绝,持续 30 秒。
30 秒过后,熔断器进入 半开状态 ,允许少量请求通过试水。如果此时请求仍然大量超时或异常,则熔断器会再次打开,重新计时 30 秒;如果请求恢复正常,则熔断器 关闭,恢复正常流量接入。
定义异常比例规则

- 比例阈值:0.2(即 20%)(当异常比例达到 20% 时触发熔断)
- 熔断时长:30 s(熔断开启后,拒绝请求的时间为 30 秒)
- 统计时长:5000 ms(统计窗口期为 5 秒,在此时间内采集指标)
- 最小请求数:10(统计时长内请求数至少达到 10 次,才会计算比例并触发熔断)
统计 5 秒钟内,如果请求数达到 10 个,且其中异常请求(如抛异常、超时等)的比例达到 20%,则触发熔断。
熔断开启后,后续请求将被直接拒绝,持续 30 秒。
30 秒过后,熔断器进入 半开状态,允许少量请求通过试水。如果此时请求仍然大量异常,则熔断器会再次打开,重新计时 30 秒;如果请求恢复正常,则熔断器 关闭,恢复正常流量接入。
定义异常数规则

- 异常数:2(统计时长内,异常请求总数达到 2 次时触发熔断)
- 熔断时长:30 s(熔断开启后,拒绝请求的时间为 30 秒)
- 统计时长:5000 ms(统计窗口期为 5 秒,在此时间内采集指标)
- 最小请求数:10(统计时长内请求数至少达到 10 次,才会计算并触发熔断)
统计 5 秒钟内,如果请求数达到 10 个,且其中异常请求(如抛异常、超时等)的总数达到 2 次,则触发熔断。
熔断开启后,后续请求将被直接拒绝,持续 30 秒。
30 秒过后,熔断器进入 半开状态 ,允许少量请求通过试水。如果此时请求仍然大量异常,则熔断器会再次打开,重新计时 30 秒;如果请求恢复正常,则熔断器 关闭,恢复正常流量接入。
blockHandler 、fallback
当sentinel的流控规则和熔断规则触发后就会进入blockHandler或者是fallback。
它们的区别是blockHandler只能处理sentinel的异常,也就是:
text
BlockException (被流控/熔断/降级的父类)
├─ FlowException // 【流控异常】触发了 QPS 或线程数限流规则
├─ ParamFlowException // 【热点参数异常】触发了热点参数限流规则
├─ DegradeException // 【熔断降级异常】(旧版) 触发了平均RT、异常比例或异常数降级规则
├─ SystemBlockException // 【系统保护异常】触发了系统自适应限流(如 Load 过高、CPU 使用率过高、入口 QPS 过高等)
├─ AuthorityException // 【授权异常】触发了黑白名单规则(来源访问权限被拒绝)
└─ CircuitBreakerException // 【断路器异常】(新版本 1.8.0+) 触发了新版熔断规则(慢调用比例或异常比例)
fallback 默认可以处理所有未被 exceptionsToIgnore 排除的异常比如:Throwable的所有子类异常。
但是他们的用法是一样的:
java
================
blockHandler
================
/**
* 正常远程调用。
*/
@SentinelResource(value = "getNormalOrder", blockHandler = "getNormalOrderFallback")
@Override
public OrderInfo getNormalOrder(Long productId) {
// 表面是Java方法调用,实际由Feign代理对象转换成HTTP请求
Product product = productFeignClient.getProductById(productId);
return buildOrder(productId, product);
}
// 兜底回调
public OrderInfo getNormalOrderFallback(Long productId, BlockException e) {
OrderInfo orderInfo = new OrderInfo();
orderInfo.setOrderId(0L);
orderInfo.setOrderName("未知用户订单,异常信息:" + e.toString());
return orderInfo;
}
================
fallback
================
/**
* 正常远程调用。
*/
@SentinelResource(value = "getNormalOrder", fallback = "getNormalOrderFallback")
@Override
public OrderInfo getNormalOrder(Long productId) {
int i = 10/0; // 抛出业务异常
return buildOrder(productId, product);
}
// 兜底回调
public OrderInfo getNormalOrderFallback(Long productId, Throwable e) {
OrderInfo orderInfo = new OrderInfo();
orderInfo.setOrderId(0L);
orderInfo.setOrderName("未知用户订单,异常信息:" + e.toString());
return orderInfo;
}