Sentinel 流量控制与熔断降级
导学
Resilience4j 熔断器上线后,系统稳定性明显提升。但产品经理孔蓝又来了:"双十二我们要搞秒杀活动,预计 QPS 是平时的 10 倍。能不能让系统在超过承载能力时,直接拒绝多余的流量,而不是等系统崩溃后再熔断?"
白歌点点头:"这是限流(Rate Limiting)的诉求。阿里巴巴开源的 Sentinel 在这方面比 Resilience4j 更强大——它把限流作为一等公民,而且提供实时监控面板。"
定位与问题场景
| 需求 | Resilience4j 方案 | Sentinel 方案 |
|---|---|---|
| 限流 | Rate Limiter(简单 QPS 限制) | 多种限流策略(QPS/并发线程数/热点参数/集群限流) |
| 熔断 | 基于失败率的简单熔断 | 基于响应时间、慢调用比例、异常比例的多维熔断 |
| 流量整形 | 无 | 匀速排队(漏桶)、Warm Up(令牌桶预热) |
| 实时监控 | 需通过 Micrometer → Prometheus → Grafana | 内置 Sentinel Dashboard |
| 规则动态下发 | 需重启或通过配置中心 | Dashboard 实时推送 + Nacos 持久化 |
Sentinel 核心原理
整体架构
核心概念
| 概念 | 说明 |
|---|---|
| 资源(Resource) | 被保护的代码块或接口,如 /order/create |
| 规则(Rule) | 针对资源的限流/熔断策略 |
| Entry | 进入资源的令牌,SphU.entry("resourceName") |
| Slot Chain | 责任链模式,每个 Slot 负责一种检查 |
| Context | 调用上下文,携带调用来源等信息 |
限流算法对比
完整示例:飞翔科技秒杀接口限流与熔断
场景描述
秒杀活动期间,订单创建接口 QPS 从平时的 200 飙升至 2000。使用 Sentinel 限流 + 熔断保护。
操作前后对比:
| 维度 | 引入 Sentinel 前 | 引入 Sentinel 后 |
|---|---|---|
| 秒杀 QPS 2000 | 数据库连接池耗尽,全站崩溃 | 超出 QPS 阈值直接拒绝,核心链路正常 |
| 支付服务慢 | 线程堆积,OOM | 慢调用比例熔断,快速降级 |
| 监控 | 人工巡检日志 | Dashboard 实时查看 QPS/RT |
步骤一:依赖引入
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
步骤二:基础配置
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 # Sentinel Dashboard 地址
port: 8719 # 本机与 Dashboard 通信端口
eager: true # 饥饿加载,启动时注册
步骤三:限流——@SentinelResource 注解
@RestController
public class SeckillController {
@Autowired
private OrderService orderService;
@GetMapping("/seckill/{productId}")
@SentinelResource(
value = "seckill", // 资源名
blockHandler = "seckillBlocked", // 限流/降级触发时调用的方法
fallback = "seckillFallback" // 业务异常时调用的方法
)
public Map<String, Object> seckill(
@PathVariable Long productId,
@RequestParam Long userId) {
return orderService.createSeckillOrder(userId, productId);
}
// blockHandler:仅处理限流/降级异常(BlockException)
public Map<String, Object> seckillBlocked(
Long productId, Long userId, BlockException e) {
return Map.of(
"code", 429,
"message", "秒杀活动太火爆了,请稍后再试!"
);
}
// fallback:处理业务异常(不含 BlockException)
public Map<String, Object> seckillFallback(
Long productId, Long userId, Throwable e) {
log.error("秒杀业务异常", e);
return Map.of("code", 500, "message", "系统异常");
}
}
步骤四:Sentinel Dashboard 配置规则
在 Dashboard(http://localhost:8080)中配置:
限流规则:
| 资源名 | 阈值类型 | QPS 阈值 | 流控模式 | 效果 |
|---|---|---|---|---|
| seckill | QPS | 500 | 直接 | 超过 500 QPS 直接拒绝 |
熔断规则(慢调用比例):
| 资源名 | 策略 | 最大 RT | 比例阈值 | 熔断时长 |
|---|---|---|---|---|
| seckill | 慢调用比例 | 200ms | 0.5 | 10s |
当 50% 的请求响应时间超过 200ms 时触发熔断,10 秒后进入半开状态。
步骤五:代码配置规则(不依赖 Dashboard)
@PostConstruct
public void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("seckill");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // QPS 模式
rule.setCount(500); // 阈值 500
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
@PostConstruct
public void initDegradeRules() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource("seckill");
rule.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType());
rule.setCount(200); // 最大 RT 200ms
rule.setSlowRatioThreshold(0.5); // 慢调用比例 50%
rule.setTimeWindow(10); // 熔断时长 10s
rule.setMinRequestAmount(5);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
步骤六:Feign 集成 Sentinel
feign:
sentinel:
enabled: true # 开启 Feign 的 Sentinel 支持
@FeignClient(
name = "FEIXIANG-INVENTORY-SERVICE",
fallback = InventoryClientFallback.class
)
public interface InventoryClient {
@GetMapping("/inventory/{productId}")
StockDTO getStock(@PathVariable("productId") Long productId);
}
易错场景
1. blockHandler 和 fallback 混淆
// blockHandler:仅处理 BlockException(限流/降级触发)
public Map blockHandler(Params..., BlockException e) { }
// fallback:处理所有异常(不含 BlockException)
public Map fallback(Params..., Throwable e) { }
// 两者可以同时指定,分别处理不同异常
@SentinelResource(value = "seckill", blockHandler = "...", fallback = "...")
2. blockHandler 方法签名错误
// ❌ 错误:缺少 BlockException 参数
public Map blockHandler(Long productId) { ... }
// ✅ 正确:原参数 + BlockException
public Map blockHandler(Long productId, Long userId, BlockException e) { ... }
3. Sentinel Dashboard 配置的规则重启丢失
Dashboard 中的规则默认存储在内存中,服务重启后丢失。生产环境必须持久化:
spring:
cloud:
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848
data-id: ${spring.application.name}-sentinel-rules
group-id: DEFAULT_GROUP
data-type: json
rule-type: flow
4. 与 @Transactional 混用时降级方法不生效
@SentinelResource 和 @Transactional 同方法时,由于 AOP 代理顺序问题,降级可能不生效。建议拆分为两个方法。
面试考点
Sentinel 的 blockHandler 和 fallback 有什么区别?
blockHandler仅处理 Sentinel 规则触发(限流/降级)的BlockException,方法签名需额外加BlockException参数。fallback处理业务方法抛出的所有异常(不含BlockException),方法签名仅与原方法参数一致。两者可同时使用,分别覆盖不同异常类型。
Sentinel 滑动窗口和令牌桶/漏桶的区别?
- 滑动窗口:统计最近时间窗口内的请求数,超过阈值则限流。Sentinel 默认使用滑动窗口实现 QPS 限流
- 令牌桶:以固定速率生成令牌,请求需获取令牌才放行,允许突发流量(Warm Up 效果)
- 漏桶:请求进入队列,以固定速率流出(匀速排队效果),流量整形更平滑
- Sentinel 同时支持三种效果:直接拒绝(滑动窗口)、Warm Up(令牌桶预热)、匀速排队(漏桶)
Sentinel 如何实现集群限流?
Sentinel 提供 Token Server 模式:部署独立的 Token Server 集群,所有客户端向 Token Server 请求令牌,Token Server 统一计数和限流。适合需要精确控制全局限流的场景(如整个集群秒杀接口总 QPS 不超过 5000)。
Sentinel vs Resilience4j 选型?
- Sentinel:阿里出品,限流能力突出,Dashboard 可视化,中文文档丰富,适合国内中大型项目
- Resilience4j:Netflix 推荐,轻量函数式,与 Spring 生态深度集成,适合纯熔断需求或海外项目
- 技术栈已是 Spring Cloud Alibaba(Nacos + Sentinel)的选 Sentinel;追求轻量和 Spring 原生生态的选 Resilience4j
小结
Sentinel 以限流为核心,配合熔断降级、热点参数限流、集群限流等功能,在前置流量整形方面比 Resilience4j 更精细。实时 Dashboard 大幅降低了运维门槛,规则的动态下发避免了重启。至此,服务调用链路的前四环(注册发现 → 负载均衡 → 声明式调用 → 容错)已经完整,下一章进入配置中心——如何让几百个微服务的配置统一管理。