目录

Sentinel 从入门到生产:流控、熔断、热点参数、Feign 降级与 Nacos 规则持久化

前言

一、Sentinel 的工作模型

1. 资源 Resource

HTTP 接口资源

Java 方法资源

2. 规则 Rule

3. 统计数据 Metric

4. BlockException

二、在 Spring Cloud 项目中接入 Sentinel

三、普通流量控制

1. QPS 限流

QPS 适合保护什么?

2. 线程数限流

为什么 QPS 不高也可能需要线程数限流?

四、三种流控模式

1. 直接模式

2. 关联模式

适用场景

3. 链路模式

4. 链路模式为什么要关闭 Web Context 统一?

常见错误

五、三种流控效果

1. 快速失败

2. Warm Up

3. 排队等待

六、熔断降级

流控

熔断

1. 慢调用比例

2. 异常比例

3. 异常数

4. 熔断器状态机

三个状态的含义

七、热点参数限流

典型应用

八、授权规则

注意

九、系统保护规则

十、@SentinelResource:方法级保护

1. blockHandler

2. fallback

3. blockHandler 与 fallback 的区别

4. 外部处理类

5. 同类内部调用问题

十一、全局 Sentinel 异常处理

十二、Feign 调用降级

为什么推荐 FallbackFactory?

Feign fallback 和 Sentinel fallback 的区别

十三、为什么规则必须持久化?

十四、使用 Nacos 持久化 Sentinel 规则

其他规则的数据源配置

十五、为什么仅配置 Nacos 还不够?

Nacos 链路

默认 Dashboard 链路

十六、Dashboard 接入 Nacos 的核心原理

Provider

Publisher

十七、Dashboard 源码改造思路

1. 将 Nacos 依赖改为正式依赖

2. 将 Nacos 示例代码迁入主代码

3. 配置 Nacos 地址和命名空间

4. 将 FlowControllerV2 切换到 Nacos

5. 前端菜单切换到 V2

十八、改造后的完整链路

查询规则

新增或修改规则

为什么 Publisher 发布完整规则列表?

十九、如何完整验证规则持久化

第一层:Dashboard 页面

第二层:Nacos 配置

第三层:客户端规则

第四层:真实压测

重启验证

二十、常见问题与排查思路

1. Dashboard 有规则,但接口不生效

2. Nacos 有规则,但客户端加载不到

3. 链路模式不生效

4. blockHandler 已执行,但 JMeter 仍显示成功

5. fallback 没执行

6. Dashboard 修改后 Nacos 没变化

7. 服务重启后规则丢失

二十一、生产环境设计建议

1. 不要凭感觉设置阈值

2. 网关和服务层分层保护

3. 降级结果必须可识别

4. fallback 必须轻量

5. 单机阈值不等于集群总阈值

6. 配置中心要做安全隔离

二十二、常见面试问题

1. Sentinel 的核心工作原理是什么?

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

3. QPS 限流和线程数限流有什么区别?

4. 直接、关联、链路三种流控模式有什么区别?

5. blockHandler 和 fallback 有什么区别?

6. 为什么需要 Nacos 持久化?

7. Provider 和 Publisher 分别负责什么?

8. 为什么还要修改 Dashboard 前端路由?

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

字段含义:

配置含义
dashboardSentinel Dashboard 地址
portSentinel 客户端命令通信端口
spring.application.nameDashboard 中显示的应用名

启动应用并访问一次接口后,可以在 Dashboard 中看到应用和资源。


三、普通流量控制

普通流控是 Sentinel 最基础、使用频率最高的能力。

Sentinel 支持两种主要阈值类型:

阈值类型grade含义
线程数0当前同时执行资源的请求数
QPS1每秒请求数量

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 Up1阈值逐步提升
排队等待2请求按固定速率通过

1. 快速失败

超过阈值后立即抛出:

FlowException

适合:

查询接口
对延迟敏感的业务
不允许请求排队的接口
可以由客户端稍后重试的请求

2. Warm Up

服务刚启动时,很多组件可能还没完全预热:

JIT 尚未完成
缓存为空
连接池刚建立
本地索引尚未加载

如果瞬间放入全部流量,容易导致服务刚启动就被打垮。

Warm Up 会让允许通过的流量逐步增加:

低阈值
  ↓
逐渐升高
  ↓
达到目标阈值

适合:

服务发布
缓存预热
连接池预热
冷启动明显的业务

3. 排队等待

排队等待会将突发流量调整为较平稳的速率:

大量请求瞬间到达
        ↓
Sentinel 控制匀速放行
        ↓
部分请求在限定时间内等待

适合:

消息发送
日志处理
批量数据写入
调用吞吐固定的下游系统

它不适合强实时接口,因为等待会增加响应时间。


六、熔断降级

流控和熔断经常被混淆。

流控

请求太多
→ 拒绝部分新请求

它主要保护当前资源的承载能力。

熔断

下游持续变慢或持续报错
→ 暂停调用该资源
→ 一段时间后尝试恢复

它主要防止异常服务拖垮整个调用链。

Sentinel 提供三种熔断策略:

策略gradecount 含义
慢调用比例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 的区别

对比项blockHandlerfallback
处理对象Sentinel 拦截异常业务异常
是否执行核心业务通常未执行已进入业务方法
典型异常FlowExceptionRuntimeException
使用目的规则拦截兜底业务失败降级

可以记成:

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远程服务调用
全局 BlockExceptionHandlerSpring MVC URL 资源
blockHandlerSentinel 规则拦截

它们处于不同层级,不能相互替代。


十三、为什么规则必须持久化?

直接在 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
ConfigServiceNacos 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,而不是只会在控制台中新增一条限流规则。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐