Sentinel限流

第一步:没有 Sentinel 时,我们怎么写限流?

假设你有一个查询订单的接口。为了防止数据库被打垮,你想限制每秒最多 100 个请求查数据库。

最原始的思路是用一个计数器:

java 复制代码
@Service
public class OrderService {
    
    // 记录当前正在查数据库的请求数
    private AtomicInteger counter = new AtomicInteger(0);
    // 阈值写死
    private static final int MAX = 100;
    
    public Order queryOrder(Long orderId) {
        // 1. 先判断当前请求数是否超过阈值
        int current = counter.incrementAndGet();
        if (current > MAX) {
            counter.decrementAndGet();
            throw new RuntimeException("系统繁忙,请稍后再试");
        }
        
        try {
            // 2. 执行业务:查数据库
            return orderMapper.selectById(orderId);
        } finally {
            // 3. 不管成功失败,计数器减回去
            counter.decrementAndGet();
        }
    }
}

这种写法有什么弊端?

  1. 代码侵入严重:每个需要保护的方法都要复制粘贴这段计数器逻辑,业务代码和限流代码混在一起。

  2. 无法动态调整MAX = 100 写死在代码里,线上流量突增时想改成 200,必须重启应用。

  3. 只是粗糙的并发数限制:你无法按"每秒 QPS"限流,也无法实现"预热"、"匀速排队"等精细策略。

  4. 没有监控:你不知道现在实际 QPS 是多少,有没有触发限流,系统状态是黑盒。

  5. 没有熔断能力:如果数据库已经挂了,这段代码还是会不断尝试查库,浪费资源。

因为这些弊端,所以需要一个专门的限流框架来接管这些横切逻辑。 Sentinel 就是为此设计的。


第二步:Sentinel 的设计思路------它到底做了什么?

Sentinel 的核心思想是:你只管标记"这段代码需要保护",限流的判断、拦截、统计全部交给框架。

它抽象出三个核心概念:

概念 含义 为什么需要它
资源(Resource) 被保护的代码块,用字符串命名,如 "queryOrder" 没有资源名,框架不知道你要保护哪段代码
规则(Rule) 对这个资源设置限制,如"每秒最多 100 次" 没有规则,框架不知道限制条件是什么
Slot Chain(处理链) Sentinel 内部的责任链,依次做统计、限流判断、熔断判断 没有处理链,所有逻辑耦合在一起,无法扩展

执行流程是这样的:

java 复制代码
你的业务方法
    ↓
@SentinelResource("queryOrder")  ← 标记资源
    ↓
Sentinel 拦截
    ↓
Slot Chain 处理:
  1. 统计当前 QPS
  2. 检查是否满足限流规则
  3. 如果不满足 → 抛出 BlockException
  4. 如果满足 → 放行执行业务
    ↓
业务代码执行

第三步:Spring Boot 集成 Sentinel 的完整流程

3.1 引入依赖

Sentinel 为 Spring Boot 提供了专门的 starter,没有它,你需要手动配置 Sentinel 的初始化、AOP 切面、Web 过滤器等几十行代码。

XML 复制代码
<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Sentinel 核心 + Spring Boot 自动配置 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        <version>2022.0.0.0</version>
    </dependency>
    
    <!-- 如果要使用 @SentinelResource 注解 -->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-annotation-aspectj</artifactId>
        <version>1.8.8</version>
    </dependency>
</dependencies>

为什么需要 sentinel-annotation-aspectj

因为 @SentinelResource 是基于 AOP(面向切面编程)实现的。没有它,注解不会被拦截,限流逻辑无法织入你的方法。

3.2 配置文件

bash 复制代码
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8858  # Sentinel 控制台地址(可选,用于可视化监控)
      eager: true  # 取消懒加载,应用启动时就初始化 Sentinel

为什么配置 eager: true

默认情况下 Sentinel 是懒加载的,第一次请求时才初始化。

如果第一次请求就是高并发,初始化期间的请求可能绕过限流规则。设置为 true 保证启动时就准备好。

3.3 定义资源(注解方式)

为什么不直接在代码里写 SphU.entry(),而是用注解?

因为注解通过 AOP 实现,限流逻辑和业务逻辑完全解耦。没有注解,你的每个方法都要写 try-catch,代码臃肿且难以维护。

java 复制代码
@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    /**
     * value = "queryOrder":资源名,Sentinel 通过这个名称找到对应的限流规则
     * blockHandler = "queryOrderBlock":被限流时执行的方法名
     */
    @SentinelResource(
        value = "queryOrder",
        blockHandler = "queryOrderBlock"
    )
    public Order queryOrder(Long orderId) {
        // 这里只有纯粹的业务逻辑,没有任何限流代码
        return orderMapper.selectById(orderId);
    }
    
    /**
     * 降级方法:当 queryOrder 触发限流时,会调用这个方法
     * 参数必须与原方法一致,最后加一个 BlockException 参数
     */
    public Order queryOrderBlock(Long orderId, BlockException ex) {
        // 返回降级数据,而不是抛异常让前端报 500
        Order fallbackOrder = new Order();
        fallbackOrder.setOrderId(orderId);
        fallbackOrder.setStatus("SYSTEM_BUSY");
        return fallbackOrder;
    }
}

blockHandler 为什么必须存在?

如果没有它,Sentinel 默认会抛出 FlowException,Spring Boot 会把这个异常转成 500 错误返回给前端。这对用户不友好。

blockHandler 让你有机会返回一个友好的降级结果(如"系统繁忙"提示)。

3.4 配置限流规则

规则告诉 Sentinel:资源 "queryOrder" 的限流条件是什么。

为什么不把规则写死在注解里?

因为规则需要动态调整。线上 QPS 阈值可能需要根据流量随时修改,如果写在注解里,改一个数字就要重新打包部署。

Sentinel 支持多种规则来源,初学者先理解代码硬编码 的方式(生产环境通常配 Nacos 动态推送)。

java 复制代码
@Component
public class SentinelRuleConfig {
    
    @PostConstruct
    public void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        
        FlowRule rule = new FlowRule();
        // 必须和 @SentinelResource 的 value 完全一致
        rule.setResource("queryOrder");
        // 限流类型:QPS 模式(还有线程数模式)
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 阈值:每秒最多允许 100 次请求
        rule.setCount(100);
        // 限流效果:直接拒绝(还有 WarmUp 预热、匀速排队等)
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

3.5 Controller 层调用

java 复制代码
@RestController
@RequestMapping("/order")
public class OrderController {
    
    @Autowired
    private OrderService orderService;
    
    @GetMapping("/{orderId}")
    public Order getOrder(@PathVariable Long orderId) {
        return orderService.queryOrder(orderId);
    }
}

第四步:测试验证

启动应用后,Sentinel 已经生效。你可以用 JMeter 或写个简单脚本压测:

java 复制代码
@Test
public void testLimit() throws InterruptedException {
    for (int i = 0; i < 200; i++) {
        new Thread(() -> {
            Order order = orderService.queryOrder(1L);
            System.out.println(order.getStatus());
        }).start();
    }
    Thread.sleep(5000);
}

输出中你会看到:

  • 前 100 个左右返回正常订单数据

  • 后面的请求返回 SYSTEM_BUSY(即 queryOrderBlock 的降级结果)


第五步:为什么还需要 Sentinel 控制台?

上面的代码能跑,但有一个问题:规则是硬编码的,改阈值要重启应用。

所以 Sentinel 提供了控制台(Dashboard),它是一个独立的 Web 应用,可以让你:

  1. 实时查看每个资源的 QPS、通过量、拒绝量

  2. 动态修改限流规则,无需重启

  3. 配置熔断规则(当错误率超过阈值时自动熔断)


一般不需要安装一个特别复杂的软件,下载 Sentinel Dashboard 的 jar 包,然后启动它即可

启动控制台:

复制代码
java -jar sentinel-dashboard-1.8.8.jar --server.port=8858

然后在你的 application.yml 里配置 spring.cloud.sentinel.transport.dashboard=localhost:8858,应用就会向控制台注册。

默认登录账号通常是:sentinel

密码:sentinel

【注意】:只有配置 Spring Boot 项目连接 Dashboard

java 复制代码
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8858

然后启动你的 Spring Boot 项目。

这时候你的项目会把 Sentinel 的资源、流量统计等信息发送给 Dashboard。


完整逻辑回顾

步骤 解决了什么问题 如果没有它,会怎样
引入 sentinel-spring-boot-starter 自动完成 Sentinel 初始化 手动写几十行配置代码
使用 @SentinelResource 通过 AOP 解耦限流与业务 每个方法复制粘贴 try-catch
配置 blockHandler 被限流时优雅降级 前端收到 500 错误
定义 FlowRule 声明限流阈值和策略 Sentinel 不知道限制条件,限流不生效
接入 Dashboard 动态调整规则 + 监控 改规则必须重启,系统状态不可见
相关推荐
snow@li4 天前
Sentinel:流量防卫兵/熔断降级
sentinel
LayZhangStrive5 天前
后端通识 - 远程服务调用RPC
网络·网络协议·rpc·sentinel·openfeign·远程服务调用
她说可以呀8 天前
Sentinel 授权规则
开发语言·sentinel
她说可以呀8 天前
Sentinel 流控规则 · 流控模式
sentinel
她说可以呀9 天前
Sentinel 热点规则
sentinel
智码看视界9 天前
Day 45 | Sentinel流量治理:从限流到熔断降级再到系统自适应保护
sentinel·高并发·熔断降级·流量控制·微服务治理·热点参数限流
cfm_291410 天前
了解Sentinel
分布式·架构·sentinel
Bruce180110 天前
Sentinel 限流熔断学习:配置策略、限流算法对比与适用场景
sentinel
成为你的宁宁10 天前
【Sentinel部署与流量防护】
sentinel