Skip to content
清晨的一缕阳光
返回

Sentinel 流量控制

Sentinel 流量控制

流量控制原理

滑动窗口算法

Sentinel 基于滑动窗口算法实现流量统计:

时间轴:
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│  1  │  2  │  3  │  4  │  5  │  6  │  7  │  8  │  9  │ 10  │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
                    ↑              ↑
                  窗口开始        窗口结束

当前窗口统计:slot 5-10 的请求总数

令牌桶算法

用于实现匀速排队和预热:

令牌桶:
┌─────┬─────┬─────┬─────┬─────┐
│  🪙  │  🪙  │  🪙  │  🪙  │  🪙  │  ← 固定容量
└─────┴─────┴─────┴─────┴─────┘

  添加令牌(固定速率)

  请求获取令牌

  有令牌:通过
  无令牌:拒绝/等待

QPS 限流

基础配置

@Configuration
public class QpsFlowRuleConfig {
    
    @PostConstruct
    public void initRules() {
        List<FlowRule> rules = new ArrayList<>();
        
        // 基础 QPS 限流
        FlowRule rule = new FlowRule();
        rule.setResource("getUserById");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(100);  // 每秒最多 100 次请求
        rule.setLimitApp("default");  // 针对所有调用来源
        
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

限流模式

1. 直接拒绝(默认)

FlowRule rule = new FlowRule();
rule.setResource("getUserById");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100);
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);

2. 冷启动(预热)

FlowRule rule = new FlowRule();
rule.setResource("getUserById");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100);
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
rule.setWarmUpPeriodSec(10);  // 预热 10 秒

预热过程

3. 排队等待(匀速排队)

FlowRule rule = new FlowRule();
rule.setResource("createOrder");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100);
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
rule.setMaxQueueingTimeMs(500);  // 最大排队 500ms

适用场景

4. 预热 + 排队

FlowRule rule = new FlowRule();
rule.setResource("createOrder");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100);
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER);
rule.setWarmUpPeriodSec(10);
rule.setMaxQueueingTimeMs(500);

线程数限流

基础配置

@Configuration
public class ThreadFlowRuleConfig {
    
    @PostConstruct
    public void initRules() {
        List<FlowRule> rules = new ArrayList<>();
        
        FlowRule rule = new FlowRule();
        rule.setResource("processData");
        rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);  // 线程数模式
        rule.setCount(50);  // 最多 50 个并发线程
        rule.setLimitApp("default");
        
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

适用场景

与 QPS 限流对比

特性QPS 限流线程数限流
统计维度请求数并发线程数
适用场景快速响应接口耗时操作
资源消耗
控制精度

热点参数限流

配置规则

@Configuration
public class ParamFlowRuleConfig {
    
    @PostConstruct
    public void initRules() {
        List<ParamFlowRule> rules = new ArrayList<>();
        
        ParamFlowRule rule = new ParamFlowRule();
        rule.setResource("getUserById");
        rule.setParamIdx(0);  // 对第一个参数限流
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(10);  // 每个参数值每秒最多 10 次
        rule.setDurationInSec(1);
        
        // 针对特定参数值设置不同限流
        List<ParamFlowItem> items = new ArrayList<>();
        
        // VIP 用户:每秒 100 次
        ParamFlowItem vipItem = new ParamFlowItem();
        vipItem.setObject(String.valueOf(1));  // 用户 ID=1
        vipItem.setCount(100);
        items.add(vipItem);
        
        // 普通用户:每秒 10 次
        ParamFlowItem normalItem = new ParamFlowItem();
        normalItem.setObject(String.valueOf(2));  // 用户 ID=2
        normalItem.setCount(10);
        items.add(normalItem);
        
        rule.setParamFlowItemList(items);
        rules.add(rule);
        ParamFlowRuleManager.loadRules(rules);
    }
}

使用场景

1. 商品详情页限流

@GetMapping("/product/{id}")
@SentinelResource(
    value = "productDetail",
    blockHandler = "handleParamBlock"
)
public Result<Product> getProduct(@PathVariable Long id) {
    return Result.success(productService.findById(id));
}

// 热点商品单独限流
@Configuration
public class ProductParamRuleConfig {
    
    @PostConstruct
    public void init() {
        ParamFlowRule rule = new ParamFlowRule();
        rule.setResource("productDetail");
        rule.setParamIdx(0);  // 商品 ID 参数
        rule.setCount(100);  // 默认每个商品每秒 100 次
        
        // 热门商品限制更严格
        List<ParamFlowItem> hotItems = Arrays.asList(
            createItem(1001L, 50),   // 商品 1001:50 QPS
            createItem(1002L, 50),   // 商品 1002:50 QPS
            createItem(1003L, 50)    // 商品 1003:50 QPS
        );
        rule.setParamFlowItemList(hotItems);
        
        ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
    }
    
    private ParamFlowItem createItem(Long productId, int count) {
        ParamFlowItem item = new ParamFlowItem();
        item.setObject(productId);
        item.setCount(count);
        return item;
    }
}

2. IP 限流

@GetMapping("/api/data")
@SentinelResource(
    value = "getData",
    blockHandler = "handleIpBlock"
)
public Result<Data> getData(HttpServletRequest request) {
    String ip = request.getRemoteAddr();
    return Result.success(dataService.getData(ip));
}

@Configuration
public class IpParamRuleConfig {
    
    @PostConstruct
    public void init() {
        ParamFlowRule rule = new ParamFlowRule();
        rule.setResource("getData");
        rule.setParamIdx(0);  // IP 参数
        rule.setCount(10);  // 每个 IP 每秒 10 次
        ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
    }
}

关联限流

配置关联规则

@Configuration
public class RelationFlowRuleConfig {
    
    @PostConstruct
    public void initRules() {
        List<FlowRule> rules = new ArrayList<>();
        
        FlowRule rule = new FlowRule();
        rule.setResource("createOrder");  // 被限流资源
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(100);
        rule.setLimitApp("default");
        rule.setStrategy(RuleConstant.STRATEGY_RELATE);  // 关联策略
        rule.setRefResource("payOrder");  // 关联资源
        
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

工作原理

当 payOrder 的 QPS 超过阈值时,
限流 createOrder 资源

适用场景

链路限流

配置链路规则

@Configuration
public class ChainFlowRuleConfig {
    
    @PostConstruct
    public void initRules() {
        List<FlowRule> rules = new ArrayList<>();
        
        FlowRule rule = new FlowRule();
        rule.setResource("getUserById");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(100);
        rule.setLimitApp("default");
        rule.setStrategy(RuleConstant.STRATEGY_CHAIN);  // 链路策略
        rule.setRefResource("com.example.controller.UserController");  // 入口资源
        
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

工作原理

只统计从 UserController 调用 getUserById 的流量
其他链路调用不统计

集群限流

配置集群规则

@Configuration
public class ClusterFlowRuleConfig {
    
    @PostConstruct
    public void initRules() {
        List<FlowRule> rules = new ArrayList<>();
        
        FlowRule rule = new FlowRule();
        rule.setResource("getUserById");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(1000);  // 集群总 QPS
        rule.setLimitApp("default");
        rule.setClusterMode(true);  // 启用集群模式
        
        ClusterFlowConfig clusterConfig = new ClusterFlowConfig();
        clusterConfig.setFlowId(1);  // 流控 ID
        clusterConfig.setThresholdType(0);  // 0=QPS, 1=线程数
        clusterConfig.setClientOfflineTime(2000);  // 客户端离线时间
        
        rule.setClusterConfig(clusterConfig);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

集群限流模式

1. 平均分配

cluster:
  flow:
    strategy: 0  # 平均分配

每个实例分配的 QPS = 总 QPS / 实例数

2. 单独分配

cluster:
  flow:
    strategy: 1  # 单独分配

每个实例独立统计,不共享配额

限流效果处理

1. 快速失败

public Result<User> handleBlock(Long id, BlockException ex) {
    return Result.fail("访问过于频繁,请稍后再试");
}

2. 降级处理

@SentinelResource(
    value = "getUserById",
    fallback = "handleFallback"
)
public Result<User> getUser(Long id) {
    return Result.success(userService.findById(id));
}

public Result<User> handleFallback(Long id, Throwable ex) {
    // 可以记录日志
    log.error("获取用户失败", ex);
    return Result.fail("服务暂时不可用");
}

3. 排队等待

FlowRule rule = new FlowRule();
rule.setResource("createOrder");
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
rule.setMaxQueueingTimeMs(1000);  // 最多等待 1 秒

实时监控

查看监控指标

# 查看资源 QPS
curl http://localhost:8080/actuator/sentinel/metrics?resource=getUserById

# 查看限流次数
curl http://localhost:8080/actuator/sentinel/metrics?resource=getUserById&metricType=blockQps

集成 Grafana

# Prometheus 配置
management:
  metrics:
    export:
      prometheus:
        enabled: true

# Sentinel 指标
spring:
  cloud:
    sentinel:
      metric:
        charset: UTF-8
        interval: 1000  # 指标收集间隔

最佳实践

1. 限流阈值设置

2. 限流策略选择

3. 降级方案设计

4. 告警配置

@Component
public class FlowAlertListener {
    
    @Autowired
    private AlertService alertService;
    
    @Scheduled(fixedRate = 60000)  // 每分钟检查
    public void checkFlowMetrics() {
        Map<String, MetricVo> metrics = SentinelMetricService.getMetrics();
        
        metrics.forEach((resource, metric) -> {
            if (metric.getBlockQps() > 100) {
                alertService.sendAlert(
                    String.format("资源 %s 限流严重,QPS=%d, BlockQPS=%d",
                        resource, metric.getQps(), metric.getBlockQps())
                );
            }
        });
    }
}

总结

Sentinel 提供了丰富的流量控制功能,包括 QPS 限流、线程数限流、热点参数限流等。

合理配置限流规则可以有效保护系统免受突发流量影响,提高系统的稳定性和可用性。

在生产环境中,建议根据压测结果设置合理的阈值,并建立完善的监控告警机制。


分享这篇文章到:

上一篇文章
Spring Boot 常见问题排查
下一篇文章
Spring Boot 声明式事务管理