SpringCloud——Sentinel实战指南:流控熔断与生产级配置
目录
Sentinel 从入门到生产:流控、熔断、热点参数、Feign 降级与 Nacos 规则持久化
二、在 Spring Cloud 项目中接入 Sentinel
3. blockHandler 与 fallback 的区别
Feign fallback 和 Sentinel fallback 的区别
4. 将 FlowControllerV2 切换到 Nacos
4. blockHandler 已执行,但 JMeter 仍显示成功
5. blockHandler 和 fallback 有什么区别?
7. Provider 和 Publisher 分别负责什么?
9. Sentinel 和 Resilience4j 有什么区别?
可以。上一版更像“个人项目复盘记录”,下面这版改成面向所有 Java 后端开发者的通用教程,弱化具体项目、端口和个人环境,强化原理、配置、实战、排错与生产设计,可直接整理后发布。
Sentinel 从入门到生产:流控、熔断、热点参数、Feign 降级与 Nacos 规则持久化
前言
在单体应用中,一个接口变慢,影响范围可能只局限在当前应用。但在微服务系统中,一次请求通常会经过多层调用:
客户端
↓
API Gateway
↓
订单服务
↓
商品服务
↓
库存服务
↓
数据库或第三方接口
只要其中一个下游服务响应变慢,上游线程就可能持续等待。随着请求不断进入,线程池、连接池和内存会逐步耗尽,最终形成级联故障:
下游服务响应变慢
↓
上游请求大量等待
↓
线程和连接不断堆积
↓
接口开始超时
↓
更多请求重试
↓
系统整体雪崩
Sentinel 的价值,就是在系统被拖垮之前,对流量和调用链进行主动保护。
它主要解决以下问题:
| 能力 | 解决的问题 |
|---|---|
| 流量控制 | 请求数量超过系统承载能力 |
| 熔断降级 | 下游持续变慢或频繁报错 |
| 热点参数限流 | 某些特殊参数访问量异常高 |
| 授权控制 | 根据请求来源决定是否放行 |
| 系统保护 | 从应用整体负载维度保护服务 |
| 集群流控 | 多实例共享统一流量阈值 |
| 规则持久化 | 服务重启后规则仍然存在 |
一句话概括:
限流决定“允许多少请求进入”,熔断决定“是否还要继续调用异常服务”。
一、Sentinel 的工作模型
理解 Sentinel,首先要掌握四个核心概念:
资源 Resource
规则 Rule
统计数据 Metric
拦截异常 BlockException
1. 资源 Resource
Sentinel 保护的对象被称为“资源”。
资源可以是:
一个 HTTP 接口
一个 Java 方法
一次 Feign 调用
一次数据库操作
一段核心业务代码
例如:
GET /orders/1001
GET /products/2001
queryOrder
deductStock
都可以被定义为 Sentinel 资源。
HTTP 接口资源
在 Spring MVC 项目中,HTTP 接口通常会被自动识别为资源:
@RestController
@RequestMapping("/orders")
public class OrderController {
@GetMapping("/{id}")
public String queryOrder(@PathVariable Long id) {
return "orderId=" + id;
}
}
对应资源名通常为:
/orders/{id}
具体资源名可能受到 URL 清洗配置影响,因此创建规则前,应先在 Sentinel Dashboard 的“簇点链路”中确认真实资源名。
Java 方法资源
使用 @SentinelResource 可以手动定义方法级资源:
@Service
public class OrderService {
@SentinelResource("queryOrder")
public String queryOrder(Long id) {
return "orderId=" + id;
}
}
这里的资源名是:
queryOrder
2. 规则 Rule
规则描述“什么情况下拦截请求”。
Sentinel 的主要规则类型如下:
| 规则类型 | Java 类型 | 作用 |
|---|---|---|
| 流控规则 | FlowRule | 控制 QPS 或并发线程数 |
| 熔断规则 | DegradeRule | 根据慢调用或异常触发熔断 |
| 热点规则 | ParamFlowRule | 针对方法参数限流 |
| 授权规则 | AuthorityRule | 根据请求来源做黑白名单 |
| 系统规则 | SystemRule | 从应用整体负载维度保护 |
例如:
/orders 每秒最多通过 10 个请求
就是一条流控规则。
3. 统计数据 Metric
Sentinel 在处理请求时,会持续统计资源运行数据,例如:
每秒通过请求数
每秒拒绝请求数
当前并发线程数
平均响应时间
异常数量
异常比例
慢调用比例
规则不是凭空判断的,而是基于这些实时统计数据决定是否放行。
4. BlockException
当请求违反 Sentinel 规则时,Sentinel 会抛出 BlockException。
常见子类如下:
| 异常类型 | 触发场景 |
|---|---|
FlowException | 普通流控 |
DegradeException | 熔断降级 |
ParamFlowException | 热点参数限流 |
AuthorityException | 授权规则拒绝 |
SystemBlockException | 系统规则保护 |
需要特别区分:
BlockException
→ Sentinel 主动拒绝请求
RuntimeException
→ 业务代码执行后发生异常
这一区别直接决定了异常应由 blockHandler 还是 fallback 处理。
二、在 Spring Cloud 项目中接入 Sentinel
在 Spring Cloud Alibaba 项目中,通常引入:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
具体版本建议通过 Spring Cloud Alibaba BOM 统一管理,不要分别给每个依赖手写版本号。
配置 Dashboard 地址:
spring:
application:
name: order-service
cloud:
sentinel:
transport:
dashboard: localhost:8080
port: 8720
字段含义:
| 配置 | 含义 |
|---|---|
dashboard | Sentinel Dashboard 地址 |
port | Sentinel 客户端命令通信端口 |
spring.application.name | Dashboard 中显示的应用名 |
启动应用并访问一次接口后,可以在 Dashboard 中看到应用和资源。
三、普通流量控制
普通流控是 Sentinel 最基础、使用频率最高的能力。
Sentinel 支持两种主要阈值类型:
| 阈值类型 | grade | 含义 |
|---|---|---|
| 线程数 | 0 | 当前同时执行资源的请求数 |
| QPS | 1 | 每秒请求数量 |
1. QPS 限流
假设规则为:
资源:/orders
阈值类型:QPS
单机阈值:10
表示:
/orders 每秒最多允许通过约 10 个请求
超过阈值后,新请求会被快速拒绝,并抛出:
FlowException
QPS 适合保护什么?
QPS 流控适合吞吐上限比较明确的接口,例如:
商品查询
订单查询
短信发送
验证码发送
第三方 API 调用
数据库查询入口
2. 线程数限流
线程数限流关注的是:
当前有多少请求正在同时执行
例如:
线程数阈值 = 5
表示同一时间最多允许 5 个请求执行该资源。
为什么 QPS 不高也可能需要线程数限流?
假设某接口每秒只有 5 个请求,但每个请求需要执行 10 秒:
第 1 秒进入 5 个请求
第 2 秒又进入 5 个请求
第 3 秒再进入 5 个请求
由于请求没有及时结束,并发线程数会持续增加。
线程数限流特别适合:
慢 SQL
文件上传或处理
报表导出
图片处理
大模型调用
耗时远程请求
复杂计算任务
四、三种流控模式
Sentinel 流控规则支持三种主要模式:
| 模式 | strategy | 判断对象 |
|---|---|---|
| 直接模式 | 0 | 当前资源 |
| 关联模式 | 1 | 另一个关联资源 |
| 链路模式 | 2 | 指定入口到当前资源的调用链 |
1. 直接模式
直接模式是最常见的流控方式。
当前资源超过阈值
↓
限制当前资源
示例:
[
{
"resource": "/orders",
"limitApp": "default",
"grade": 1,
"count": 10,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
含义:
/orders 的 QPS 超过 10
→ /orders 被限流
2. 关联模式
关联模式的逻辑是:
当资源 B 流量过高时,限制资源 A。
例如:
/order/read 查询订单
/order/write 修改订单
希望写操作压力过高时,暂时限制部分读操作:
监控 /order/write
限制 /order/read
规则配置:
[
{
"resource": "/order/read",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 1,
"refResource": "/order/write",
"controlBehavior": 0,
"clusterMode": false
}
]
含义:
/order/write 的 QPS 超过 5
↓
/order/read 被限流
注意:
被统计的是 write
被限制的是 read
造成压力的 /order/write 本身不一定会被拦截。
适用场景
关联模式通常用于存在资源竞争的业务,例如:
写数据库压力过高时限制查询
支付处理繁忙时限制账单查询
库存扣减繁忙时限制库存统计
文件写入繁忙时限制文件扫描
3. 链路模式
链路模式解决的问题是:
同一个公共资源被多个入口调用,但只想限制其中一条调用链。
例如:
/order/read
↓
queryOrderInfo
/order/write
↓
queryOrderInfo
两个接口最终都会调用同一个方法:
@Service
public class OrderQueryService {
@SentinelResource(
value = "queryOrderInfo",
blockHandler = "queryOrderInfoBlockHandler"
)
public String queryOrderInfo(String source) {
return "查询成功,来源=" + source;
}
public String queryOrderInfoBlockHandler(
String source,
BlockException exception
) {
return "当前调用链被限流,来源=" + source;
}
}
现在只想限制:
/order/write → queryOrderInfo
不限制:
/order/read → queryOrderInfo
配置:
[
{
"resource": "queryOrderInfo",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 2,
"refResource": "/order/write",
"controlBehavior": 0,
"clusterMode": false
}
]
核心关系:
resource = queryOrderInfo
refResource = /order/write
strategy = 2
表示:
只统计从 /order/write 入口进入 queryOrderInfo 的流量
4. 链路模式为什么要关闭 Web Context 统一?
在部分 Spring Cloud Alibaba 版本中,Web 请求默认可能被统一到同一个 Sentinel Context:
sentinel_spring_web_context
这样 Sentinel 无法区分不同 URL 入口。
需要配置:
spring:
cloud:
sentinel:
web-context-unify: false
然后重启服务。
配置生效后,簇点链路中应能看到:
/order/read
└─ queryOrderInfo
/order/write
└─ queryOrderInfo
常见错误
链路规则配置了却不生效,优先检查:
web-context-unify 是否为 false
入口资源名称是否完全一致
公共方法是否真的被 @SentinelResource 代理
是否发生了同类内部自调用
五、三种流控效果
流控模式决定“统计谁”,流控效果决定“超过阈值后怎么处理”。
常见效果如下:
| 效果 | controlBehavior | 行为 |
|---|---|---|
| 快速失败 | 0 | 立即拒绝 |
| Warm Up | 1 | 阈值逐步提升 |
| 排队等待 | 2 | 请求按固定速率通过 |
1. 快速失败
超过阈值后立即抛出:
FlowException
适合:
查询接口
对延迟敏感的业务
不允许请求排队的接口
可以由客户端稍后重试的请求
2. Warm Up
服务刚启动时,很多组件可能还没完全预热:
JIT 尚未完成
缓存为空
连接池刚建立
本地索引尚未加载
如果瞬间放入全部流量,容易导致服务刚启动就被打垮。
Warm Up 会让允许通过的流量逐步增加:
低阈值
↓
逐渐升高
↓
达到目标阈值
适合:
服务发布
缓存预热
连接池预热
冷启动明显的业务
3. 排队等待
排队等待会将突发流量调整为较平稳的速率:
大量请求瞬间到达
↓
Sentinel 控制匀速放行
↓
部分请求在限定时间内等待
适合:
消息发送
日志处理
批量数据写入
调用吞吐固定的下游系统
它不适合强实时接口,因为等待会增加响应时间。
六、熔断降级
流控和熔断经常被混淆。
流控
请求太多
→ 拒绝部分新请求
它主要保护当前资源的承载能力。
熔断
下游持续变慢或持续报错
→ 暂停调用该资源
→ 一段时间后尝试恢复
它主要防止异常服务拖垮整个调用链。
Sentinel 提供三种熔断策略:
| 策略 | grade | count 含义 |
|---|---|---|
| 慢调用比例 | 0 | 慢调用 RT 阈值,单位毫秒 |
| 异常比例 | 1 | 异常比例,范围 0~1 |
| 异常数 | 2 | 异常请求数量 |
1. 慢调用比例
示例接口:
@GetMapping("/test/slow")
public String slow(
@RequestParam(defaultValue = "1000") long delay
) throws InterruptedException {
Thread.sleep(delay);
return "success";
}
规则:
[
{
"resource": "/test/slow",
"grade": 0,
"count": 500,
"timeWindow": 10,
"minRequestAmount": 5,
"statIntervalMs": 1000,
"slowRatioThreshold": 0.5
}
]
含义:
统计窗口为 1 秒
至少完成 5 个请求
响应时间超过 500ms 算慢调用
慢调用比例达到 50%
熔断时间为 10 秒
触发后,新请求不会继续执行 Thread.sleep(),而是快速返回 DegradeException。
2. 异常比例
示例接口:
@GetMapping("/test/exception")
public String exception(
@RequestParam(defaultValue = "true") boolean fail
) {
if (fail) {
throw new RuntimeException("模拟业务异常");
}
return "success";
}
规则:
[
{
"resource": "/test/exception",
"grade": 1,
"count": 0.5,
"timeWindow": 10,
"minRequestAmount": 5,
"statIntervalMs": 1000
}
]
含义:
统计周期内至少有 5 个请求
异常比例达到 50%
熔断 10 秒
测试时通常会看到两类响应:
| 响应 | 含义 |
|---|---|
| HTTP 500 | 请求进入业务方法后发生异常 |
| HTTP 503 | 熔断器已经打开,请求未进入业务方法 |
3. 异常数
规则:
[
{
"resource": "/test/exception",
"grade": 2,
"count": 5,
"timeWindow": 10,
"minRequestAmount": 5,
"statIntervalMs": 1000
}
]
表示统计窗口内异常数达到 5 时触发熔断。
异常数策略适合对错误次数非常敏感的场景,例如:
支付请求
短信通道调用
第三方计费接口
数据库写操作
4. 熔断器状态机
熔断器通常经历三个状态:
CLOSED
↓ 达到熔断条件
OPEN
↓ 熔断时间结束
HALF_OPEN
↓ 探测请求成功
CLOSED
如果半开状态下的探测请求失败:
HALF_OPEN
↓
OPEN
熔断器重新打开。
三个状态的含义
| 状态 | 行为 |
|---|---|
CLOSED | 正常放行并统计 |
OPEN | 请求直接拒绝 |
HALF_OPEN | 放行少量请求探测服务是否恢复 |
七、热点参数限流
普通流控保护整个资源:
/products/{productId}
但现实中,不同参数值的访问压力可能差别很大:
productId=1001:普通商品
productId=8888:秒杀商品
热点参数限流可以对某个方法参数单独设置规则。
@SentinelResource(
value = "queryProduct",
blockHandler = "queryProductBlocked"
)
public String queryProduct(Long productId) {
return "productId=" + productId;
}
public String queryProductBlocked(
Long productId,
BlockException exception
) {
return "商品访问过于频繁";
}
假设第一个参数的索引是:
paramIdx = 0
可以配置:
普通参数值:5 QPS
productId=8888:1 QPS
productId=1001:20 QPS
典型应用
热门商品
特殊用户
高频城市
特定订单类型
大客户账号
秒杀活动 ID
热点规则主要保护“参数值”,不是普通 URL 本身。
八、授权规则
授权规则根据请求来源决定是否允许访问。
需要实现 RequestOriginParser:
@Component
public class HeaderOriginParser
implements RequestOriginParser {
@Override
public String parseOrigin(
HttpServletRequest request
) {
String origin = request.getHeader("origin");
if (origin == null || origin.isBlank()) {
return "default";
}
return origin;
}
}
客户端请求时携带:
origin: web
或者:
origin: internal-service
然后可以创建:
白名单:只允许 web、internal-service
黑名单:禁止 crawler、unknown
触发授权拦截时,Sentinel 抛出:
AuthorityException
注意
授权规则只能作为基础来源控制,不能代替完整认证与授权系统。
真实项目中的身份认证仍应使用:
JWT
OAuth 2.0
Spring Security
网关统一认证
服务间签名
九、系统保护规则
系统规则从应用整体维度进行保护,而不是只针对某个资源。
常见指标包括:
应用总 QPS
入口线程数
平均响应时间
CPU 使用率
系统负载
例如:
入口线程数超过 200
→ 拒绝部分新请求
系统规则需要谨慎使用,因为它的影响范围通常比资源级规则更大。
生产实践中通常优先建立:
资源级 QPS 限流
资源级线程数保护
核心调用熔断
网关入口限流
然后再评估是否需要系统级规则。
十、@SentinelResource:方法级保护
@SentinelResource 是 Sentinel 方法级保护的核心注解。
@SentinelResource(
value = "queryOrder",
blockHandler = "queryOrderBlocked",
fallback = "queryOrderFallback"
)
public OrderDTO queryOrder(Long id) {
return orderRepository.findById(id);
}
核心属性:
| 属性 | 作用 |
|---|---|
value | 资源名称 |
blockHandler | 处理 Sentinel 拦截异常 |
fallback | 处理业务异常 |
blockHandlerClass | 外部拦截处理类 |
fallbackClass | 外部业务降级类 |
1. blockHandler
blockHandler 专门处理:
FlowException
DegradeException
ParamFlowException
AuthorityException
方法签名要求:
原方法参数
+
最后增加 BlockException
例如:
public OrderDTO queryOrderBlocked(
Long id,
BlockException exception
) {
OrderDTO result = new OrderDTO();
result.setId(id);
result.setMessage("请求被 Sentinel 拦截");
return result;
}
2. fallback
fallback 用于处理业务方法执行期间发生的异常:
public OrderDTO queryOrderFallback(
Long id,
Throwable throwable
) {
OrderDTO result = new OrderDTO();
result.setId(id);
result.setMessage("订单查询失败");
return result;
}
3. blockHandler 与 fallback 的区别
| 对比项 | blockHandler | fallback |
|---|---|---|
| 处理对象 | Sentinel 拦截异常 | 业务异常 |
| 是否执行核心业务 | 通常未执行 | 已进入业务方法 |
| 典型异常 | FlowException | RuntimeException |
| 使用目的 | 规则拦截兜底 | 业务失败降级 |
可以记成:
blockHandler:
Sentinel 不让执行
fallback:
业务执行了,但执行失败
4. 外部处理类
多个方法需要共用兜底逻辑时,可以使用外部处理类:
public final class SentinelHandlers {
private SentinelHandlers() {
}
public static String queryBlocked(
Long id,
BlockException exception
) {
return "请求被限制";
}
}
使用:
@SentinelResource(
value = "queryOrder",
blockHandlerClass = SentinelHandlers.class,
blockHandler = "queryBlocked"
)
外部类中的处理方法必须是:
public static
5. 同类内部调用问题
下面的调用可能绕过 Spring AOP 代理:
this.queryOrder(id);
更可靠的结构是:
Controller
↓
Spring 注入的 Service
↓
@SentinelResource 方法
例如:
@RestController
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@GetMapping("/orders/{id}")
public OrderDTO query(@PathVariable Long id) {
return orderService.queryOrder(id);
}
}
十一、全局 Sentinel 异常处理
对于 Spring MVC 自动识别的 URL 资源,可以实现全局 BlockExceptionHandler。
下面以 Spring Boot 3 为例,使用 jakarta.servlet。Spring Boot 2 项目通常使用 javax.servlet。
@Component
public class GlobalSentinelBlockHandler
implements BlockExceptionHandler {
private final ObjectMapper objectMapper;
public GlobalSentinelBlockHandler(
ObjectMapper objectMapper
) {
this.objectMapper = objectMapper;
}
@Override
public void handle(
HttpServletRequest request,
HttpServletResponse response,
String resourceName,
BlockException exception
) throws Exception {
int httpStatus;
int code;
String message;
if (exception instanceof ParamFlowException) {
httpStatus = 429;
code = 42902;
message = "热点参数访问过于频繁";
}
else if (exception instanceof FlowException) {
httpStatus = 429;
code = 42901;
message = "请求访问过于频繁";
}
else if (exception instanceof DegradeException) {
httpStatus = 503;
code = 50301;
message = "服务暂时不可用";
}
else if (exception instanceof AuthorityException) {
httpStatus = 403;
code = 40301;
message = "当前请求来源无权访问";
}
else if (exception instanceof SystemBlockException) {
httpStatus = 503;
code = 50302;
message = "系统繁忙";
}
else {
httpStatus = 429;
code = 42900;
message = "请求被 Sentinel 拦截";
}
Map<String, Object> body = new LinkedHashMap<>();
body.put("code", code);
body.put("message", message);
body.put(
"exceptionType",
exception.getClass().getSimpleName()
);
body.put("resource", resourceName);
body.put("path", request.getRequestURI());
body.put("timestamp", System.currentTimeMillis());
response.setStatus(httpStatus);
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
objectMapper.writeValue(
response.getWriter(),
body
);
}
}
统一异常处理可以让前端得到稳定的响应格式:
{
"code": 42901,
"message": "请求访问过于频繁",
"exceptionType": "FlowException",
"resource": "/orders",
"path": "/orders",
"timestamp": 1784880000000
}
十二、Feign 调用降级
微服务之间通常通过 OpenFeign 调用。
@FeignClient(
name = "product-service",
fallbackFactory =
ProductClientFallbackFactory.class
)
public interface ProductClient {
@GetMapping("/products/{id}")
ProductDTO queryById(@PathVariable Long id);
}
FallbackFactory:
@Component
public class ProductClientFallbackFactory
implements FallbackFactory<ProductClient> {
@Override
public ProductClient create(Throwable cause) {
return id -> {
ProductDTO result = new ProductDTO();
result.setId(id);
result.setName("暂时无法获取商品信息");
result.setDegraded(true);
return result;
};
}
}
部分版本还需要显式开启 Feign Sentinel 支持:
feign:
sentinel:
enabled: true
具体配置键以所使用的 Spring Cloud Alibaba 版本为准。
为什么推荐 FallbackFactory?
普通 fallback 只能返回降级结果,FallbackFactory 还能拿到:
Throwable cause
因此可以区分:
没有可用服务实例
连接超时
读取超时
下游返回 HTTP 500
Feign 解码失败
Sentinel 熔断
但生产环境不要把所有异常都吞掉,应至少记录:
调用服务
请求参数
异常类型
根因
链路追踪 ID
降级结果
Feign fallback 和 Sentinel fallback 的区别
| 机制 | 作用范围 |
|---|---|
@SentinelResource fallback | 当前 Java 方法 |
| Feign fallback | 远程服务调用 |
| 全局 BlockExceptionHandler | Spring MVC URL 资源 |
blockHandler | Sentinel 规则拦截 |
它们处于不同层级,不能相互替代。
十三、为什么规则必须持久化?
直接在 Dashboard 中创建规则时,默认规则往往只存在于客户端内存。
Dashboard
↓
服务实例内存
服务重启后:
JVM 退出
→ 内存清空
→ 规则丢失
这在生产环境中不可接受。
假设订单服务有三个实例:
order-service-1
order-service-2
order-service-3
仅靠 Dashboard 临时推送,会面临:
重启后规则丢失
多实例规则不一致
规则变更缺少审计
无法回滚历史版本
配置来源混乱
因此需要引入持久化配置中心,例如 Nacos。
十四、使用 Nacos 持久化 Sentinel 规则
引入依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
配置流控规则数据源:
spring:
cloud:
sentinel:
datasource:
flow-rules:
nacos:
server-addr: localhost:8848
namespace: your-namespace-id
data-id: ${spring.application.name}-flow-rules
group-id: SENTINEL_GROUP
data-type: json
rule-type: flow
Nacos 中创建:
Data ID:
order-service-flow-rules
Group:
SENTINEL_GROUP
配置内容必须是 JSON 数组:
[
{
"resource": "/orders",
"limitApp": "default",
"grade": 1,
"count": 10,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
发布后,客户端会监听 Nacos 配置变化:
Nacos 修改规则
↓
Sentinel 数据源收到变更
↓
JSON 转为 FlowRule
↓
FlowRuleManager 更新规则
不需要重启服务。
其他规则的数据源配置
可以为不同规则创建不同 Data ID:
服务名-flow-rules
服务名-degrade-rules
服务名-param-flow-rules
服务名-authority-rules
服务名-system-rules
对应 rule-type:
| 规则 | rule-type |
|---|---|
| 流控 | flow |
| 熔断 | degrade |
| 热点 | param-flow |
| 授权 | authority |
| 系统 | system |
将不同规则拆开存储,比把全部规则塞进一个配置文件更容易管理。
十五、为什么仅配置 Nacos 还不够?
只配置 Nacos 数据源后,通常会形成两条规则链路。
Nacos 链路
Nacos
↓
业务服务
默认 Dashboard 链路
Dashboard
↓
业务服务客户端内存
这会造成两个规则来源:
Nacos 中阈值 = 10
Dashboard 中临时改成 = 2
当前运行时可能变成 2,但服务重启后又会从 Nacos 加载 10。
最终表现是:
规则看起来时好时坏
重启后恢复旧值
Dashboard 与 Nacos 内容不一致
正确架构应统一为:
Dashboard
↓
Nacos
↓
业务服务
其中:
Nacos = 唯一规则存储中心
Dashboard = Nacos 的图形化管理页面
业务服务 = 规则执行者
十六、Dashboard 接入 Nacos 的核心原理
要让 Dashboard 读写 Nacos,需要两个抽象:
DynamicRuleProvider
DynamicRulePublisher
Provider
负责读取规则:
Dashboard 打开规则页面
↓
Provider 查询 Nacos
↓
JSON 转为规则对象
↓
页面显示
Publisher
负责发布规则:
Dashboard 新增或修改规则
↓
Publisher 将规则转为 JSON
↓
写入 Nacos
↓
业务服务监听到变化
可以将它们理解为:
| Sentinel 组件 | 后端类比 |
|---|---|
| Provider | 查询 Service |
| Publisher | 保存 Service |
| ConfigService | Nacos DAO 客户端 |
| NacosConfigUtil | 常量类 |
| Controller | 管理接口 |
十七、Dashboard 源码改造思路
默认 Dashboard 中通常已经有 Nacos 示例代码,但可能位于测试目录,并且 Nacos 依赖也是测试范围。
通用改造步骤如下。
1. 将 Nacos 依赖改为正式依赖
原配置:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<scope>test</scope>
</dependency>
删除:
<scope>test</scope>
否则正式代码无法使用 Nacos API。
2. 将 Nacos 示例代码迁入主代码
主要包括:
NacosConfig
NacosConfigUtil
FlowRuleNacosProvider
FlowRuleNacosPublisher
职责:
| 类 | 作用 |
|---|---|
NacosConfig | 创建 ConfigService 和 JSON 转换器 |
NacosConfigUtil | 定义 Group、Data ID 后缀 |
FlowRuleNacosProvider | 从 Nacos 读取流控规则 |
FlowRuleNacosPublisher | 将流控规则写入 Nacos |
3. 配置 Nacos 地址和命名空间
sentinel.nacos.server-addr=127.0.0.1:8848
sentinel.nacos.namespace=your-namespace-id
注意:
填写的是 Namespace ID,不是页面显示名称。
Dashboard 和业务服务必须使用同一个:
Nacos 地址
Namespace
Group
Data ID
4. 将 FlowControllerV2 切换到 Nacos
默认可能使用:
@Qualifier("flowRuleDefaultProvider")
@Qualifier("flowRuleDefaultPublisher")
修改为:
@Qualifier("flowRuleNacosProvider")
@Qualifier("flowRuleNacosPublisher")
此时 V2 Controller 的查询和保存都会经过 Nacos。
5. 前端菜单切换到 V2
默认菜单可能仍然访问:
dashboard.flowV1
需要切换为:
dashboard.flow
否则:
后端 V2 已改造
但页面仍然调用 V1
新代码不会真正被使用。
十八、改造后的完整链路
查询规则
浏览器打开“流控规则”
↓
FlowControllerV2
↓
FlowRuleNacosProvider
↓
Nacos ConfigService.getConfig()
↓
读取服务名-flow-rules
↓
JSON 转为规则对象
↓
Dashboard 页面显示
新增或修改规则
Dashboard 点击保存
↓
FlowControllerV2
↓
Dashboard 内存 Repository
↓
读取当前应用完整规则列表
↓
FlowRuleNacosPublisher
↓
JSON 序列化
↓
Nacos publishConfig()
↓
业务服务动态加载
为什么 Publisher 发布完整规则列表?
Dashboard 的新增、修改、删除逻辑通常不是只发布一条增量,而是:
先更新 Dashboard 内存仓库
↓
查出当前应用的所有规则
↓
整体覆盖 Nacos 配置
因此必须注意:
Nacos 已经被别人修改
↓
Dashboard 页面仍是旧数据
↓
直接点击保存
↓
Dashboard 可能用旧列表覆盖新配置
正确操作流程:
先刷新 Dashboard
→ 确认页面已加载 Nacos 最新规则
→ 再编辑并保存
生产环境中还应考虑:
权限控制
版本校验
操作审计
历史回滚
并发编辑保护
十九、如何完整验证规则持久化
只看到 Dashboard 页面更新,并不能证明改造成功。
建议按照四层验证。
第一层:Dashboard 页面
修改阈值:
5 → 10
页面显示 10。
只能证明前端保存成功。
第二层:Nacos 配置
检查对应 Data ID:
"count": 10.0
证明 Dashboard 已写入 Nacos。
第三层:客户端规则
访问 Sentinel 客户端命令端口:
http://localhost:8720/getRules?type=flow
确认:
resource 正确
count 正确
strategy 正确
refResource 正确
证明业务服务已经动态收到规则。
第四层:真实压测
使用 JMeter 或其他工具触发流控:
阈值为 5
→ 压测出现限流
阈值改为 10000
→ 同样压力不再限流
证明规则不只是“加载了”,而且真正参与请求判断。
重启验证
最后分别重启:
业务服务
Sentinel Dashboard
重启后:
业务服务能够重新加载规则
Dashboard 能重新显示规则
才算持久化闭环。
二十、常见问题与排查思路
1. Dashboard 有规则,但接口不生效
检查:
资源名是否完全一致
规则是否加载到正确客户端
QPS 是否真的超过阈值
是否存在其他规则覆盖
是否使用了错误机器实例
先查看:
簇点链路
getRules?type=flow
不要凭 Controller 路径猜资源名。
2. Nacos 有规则,但客户端加载不到
重点核对:
server-addr
namespace
data-id
group-id
rule-type
data-type
其中最容易出错的是:
Namespace 名称和 Namespace ID 混淆
Data ID 少字符或多后缀
Group 不一致
JSON 不是数组
3. 链路模式不生效
重点检查:
web-context-unify: false
然后确认簇点链路中真的存在不同入口。
还要确认:
refResource 使用实际入口名
公共方法通过 Spring Bean 调用
没有同类 this.xxx() 自调用
4. blockHandler 已执行,但 JMeter 仍显示成功
如果 blockHandler 返回普通字符串:
return "请求已限流";
Spring MVC 仍会返回 HTTP 200。
因此 JMeter 可能全部显示绿色。
测试时需要查看:
响应体
异常类型
业务日志
通过 QPS
拒绝 QPS
若需要 HTTP 429,应在全局处理器中显式设置状态码。
5. fallback 没执行
可能原因:
发生的是 BlockException,应由 blockHandler 处理
异常被业务代码提前 catch
异常类型被 exceptionsToIgnore 忽略
方法没有经过 Spring AOP 代理
方法发生同类内部调用
6. Dashboard 修改后 Nacos 没变化
检查:
前端是否进入 V2 页面
FlowControllerV2 是否注入 Nacos Publisher
Nacos 配置地址是否正确
Namespace 是否一致
Dashboard 是否使用了重新构建后的 Jar
浏览器是否缓存了旧前端资源
修改 Dashboard 前端后,通常需要:
重新打包
重启 Dashboard
浏览器 Ctrl + F5
7. 服务重启后规则丢失
这通常说明规则只在客户端内存中,没有通过 Nacos DataSource 加载。
检查业务服务是否配置:
spring.cloud.sentinel.datasource
Dashboard 中临时创建规则,不等于已经持久化。
二十一、生产环境设计建议
1. 不要凭感觉设置阈值
阈值应来自:
压测结果
历史监控数据
线程池大小
连接池大小
下游吞吐能力
平均响应时间
错误率
例如某接口压测结果:
50 QPS:稳定
80 QPS:RT 明显上升
100 QPS:错误率快速增加
可以先将限流阈值设置在稳定区间,而不是直接设置为极限值。
2. 网关和服务层分层保护
建议形成两层保护:
网关层
→ 控制整体入口流量
服务层
→ 保护具体资源和内部方法
例如:
Gateway:限制单用户和全局 QPS
Order Service:保护订单创建接口
Stock Service:保护库存扣减方法
3. 降级结果必须可识别
不要静默返回看似正常的假数据。
建议在响应中明确标识:
{
"code": 50301,
"message": "商品服务暂时不可用",
"degraded": true,
"data": null
}
否则上游可能把降级结果当作真实数据继续处理。
4. fallback 必须轻量
错误示例:
原服务失败
→ fallback 再调用另一个不稳定服务
→ fallback 自己也阻塞
降级逻辑应该:
快速
无阻塞
少依赖
可监控
可追踪
5. 单机阈值不等于集群总阈值
假设每个实例规则都是:
10 QPS
服务有三个实例:
实例 A:10 QPS
实例 B:10 QPS
实例 C:10 QPS
集群总量可能接近:
30 QPS
普通单机流控是每实例生效。
需要统一全局配额时,应评估:
集群流控
网关统一限流
Redis + Lua 限流
服务网格限流
6. 配置中心要做安全隔离
Nacos 中的 Sentinel 规则直接影响生产流量,应至少配置:
命名空间隔离
账号权限
配置审计
历史版本
变更审批
备份与回滚
生产环境只读或受控写入
不要让所有开发人员都能随意修改生产限流阈值。
二十二、常见面试问题
1. Sentinel 的核心工作原理是什么?
Sentinel 将接口或方法定义为资源,请求进入资源时经过 Slot Chain。
Slot Chain 负责:
节点构建
统计数据
来源控制
流控判断
熔断判断
系统规则判断
规则通过则继续执行,违反规则则抛出 BlockException。
2. 限流和熔断有什么区别?
限流关注流量:
请求数量过多
→ 拒绝部分请求
熔断关注服务健康状态:
持续变慢或报错
→ 暂停调用
→ 等待恢复
3. QPS 限流和线程数限流有什么区别?
QPS 控制单位时间内的请求数量。
线程数控制当前同时执行的请求数量。
慢接口即使 QPS 不高,也可能造成线程堆积,因此更适合线程数保护。
4. 直接、关联、链路三种流控模式有什么区别?
直接:
当前资源超限,限制当前资源
关联:
关联资源超限,限制当前资源
链路:
只限制指定入口到当前资源的调用链
5. blockHandler 和 fallback 有什么区别?
blockHandler:
处理 Sentinel 拦截异常
fallback:
处理业务方法执行异常
6. 为什么需要 Nacos 持久化?
Dashboard 默认规则主要存在客户端内存。
服务重启后可能丢失,多个实例也难以统一。
Nacos 可以提供:
持久化
动态更新
多实例共享
历史版本
集中管理
7. Provider 和 Publisher 分别负责什么?
Provider:
从配置中心读取规则
Publisher:
将规则写入配置中心
8. 为什么还要修改 Dashboard 前端路由?
因为 Nacos Provider 和 Publisher 接入的是 V2 Controller,而默认菜单可能仍然访问 V1 页面。
只改后端、不切前端,页面仍不会使用新接口。
9. Sentinel 和 Resilience4j 有什么区别?
二者都能完成限流、熔断和降级。
Sentinel 更强调:
实时流量统计
Dashboard
热点参数限流
系统自适应保护
规则动态管理
Resilience4j 更偏向轻量级 Java 库和函数式装饰器模式。
实际选型要结合团队技术栈和运维体系。
总结
Sentinel 的完整请求处理链路可以概括为:
请求进入资源
↓
Sentinel 统计实时指标
↓
依次判断流控、熔断、授权等规则
↓
规则通过
→ 执行业务
规则不通过
→ 抛出 BlockException
→ blockHandler 或全局异常处理
生产级规则管理链路应为:
Sentinel Dashboard
↓
Nacos
↓
各业务服务实例
↓
Sentinel RuleManager
真正掌握 Sentinel,不是记住 Dashboard 每个按钮的位置,而是能够回答以下问题:
当前保护的资源是什么?
规则统计的是哪个资源?
阈值是 QPS、线程数还是错误比例?
请求被拦截时抛出什么异常?
异常由 blockHandler 还是 fallback 处理?
规则最终保存在哪里?
服务重启后规则是否能够恢复?
多实例的阈值是单机还是集群总量?
规则修改后如何验证真正生效?
当这些问题能够从资源、规则、调用链、异常处理和配置持久化五个层面解释清楚,才算真正掌握 Sentinel,而不是只会在控制台中新增一条限流规则。
更多推荐
所有评论(0)