Sentinel熔断降级原理与架构
微服务架构中,服务间调用链路复杂,单点故障可能引发雪崩效应。Sentinel是阿里巴巴开源的流量治理组件,通过资源维度对接口进行流量控制、熔断降级和系统自适应保护。核心思路是每个RPC调用都定义为一个资源,Sentinel在调用前后插入Slot Chain进行规则匹配和统计。
Sentinel的Slot Chain处理流程:
1. NodeSelectorSlot:构建调用树,记录每个资源的调用统计节点
2. ClusterBuilderSlot:聚合集群维度的统计数据
3. StatisticSlot:维护滑动窗口统计(QPS、RT、异常数)
4. FlowSlot:流量控制规则匹配
5. DegradeSlot:熔断降级规则匹配
6. AuthoritySlot:黑白名单鉴权
7. SystemSlot:系统级自适应限流
Spring Boot集成Sentinel
Spring Cloud Alibaba项目中集成Sentinel非常简单,添加依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2023.0.1.0</version>
</dependency>
application.yml基础配置:
spring:
cloud:
sentinel:
transport:
dashboard: 192.168.1.100:8858
port: 8719 # 与dashboard通信的端口
eager: true # 立即初始化,不等首次请求
datasource:
flow: # 规则数据源(推荐Nacos持久化)
nacos:
server-addr: 192.168.1.100:8848
dataId: ${spring.application.name}-flow-rules
groupId: SENTINEL_GROUP
rule-type: flow
eager: true让Sentinel在应用启动时就与Dashboard建立连接,而非等到首次请求。规则数据源配置在Nacos上,避免Dashboard重启后规则丢失。
流量控制规则配置
Sentinel的流控规则有两条路径:Dashboard可视化配置和代码硬编码。生产环境推荐Nacos持久化配置,本地开发可用代码方式:
@Configuration
public class SentinelRuleConfig {
@PostConstruct
public void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
// 限制 /api/orders 接口的QPS
FlowRule rule1 = new FlowRule();
rule1.setResource("/api/orders");
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setCount(100); // 阈值100 QPS
rule1.setLimitApp("default");
rule1.setStrategy(RuleConstant.STRATEGY_DIRECT); // 直接限流
rules.add(rule1);
// 限制 /api/products 接口的并发线程数
FlowRule rule2 = new FlowRule();
rule2.setResource("/api/products");
rule2.setGrade(RuleConstant.FLOW_GRADE_THREAD);
rule2.setCount(50); // 最大并发50线程
rule2.setLimitApp("default");
rules.add(rule2);
FlowRuleManager.loadRules(rules);
}
}
QPS流控和线程数流控的选择策略:
– QPS模式:限制每秒请求数,适合接口调用频率控制。配合匀速排队效果更好。
– 线程数模式:限制并发执行线程,适合慢调用保护。线程数到达阈值时新请求直接拒绝。
匀速排队模式让请求匀速通过,而非直接拒绝:
FlowRule rule = new FlowRule();
rule.setResource("/api/orders");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100);
// 匀速排队模式
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
rule.setMaxQueueingTimeMs(2000); // 排队超时2秒
熔断降级规则实战
熔断规则触发条件有三种:慢调用比例、异常比例、异常数。配置方式:
@PostConstruct
public void initDegradeRules() {
List<DegradeRule> rules = new ArrayList<>();
// 策略1:慢调用比例熔断
// RT超过200ms算慢调用,慢调用比例超过50%触发熔断
DegradeRule slowCallRule = new DegradeRule();
slowCallRule.setResource("/api/payment");
slowCallRule.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType());
slowCallRule.setCount(200); // 最大RT 200ms
slowCallRule.setSlowRatioThreshold(0.5); // 慢调用比例阈值50%
slowCallRule.setMinRequestAmount(5); // 最小请求数5
slowCallRule.setStatIntervalMs(10000); // 统计窗口10秒
slowCallRule.setTimeWindow(10); // 熔断持续时间10秒
rules.add(slowCallRule);
// 策略2:异常比例熔断
// 异常率超过30%触发
DegradeRule exceptionRatioRule = new DegradeRule();
exceptionRatioRule.setResource("/api/payment");
exceptionRatioRule.setGrade(CircuitBreakerStrategy.EXCEPTION_RATIO.getType());
exceptionRatioRule.setCount(0.3); // 异常比例阈值30%
exceptionRatioRule.setMinRequestAmount(5);
exceptionRatioRule.setStatIntervalMs(10000);
exceptionRatioRule.setTimeWindow(10);
rules.add(exceptionRatioRule);
// 策略3:异常数熔断
// 异常数超过10次触发
DegradeRule exceptionCountRule = new DegradeRule();
exceptionCountRule.setResource("/api/payment");
exceptionCountRule.setGrade(CircuitBreakerStrategy.EXCEPTION_COUNT.getType());
exceptionCountRule.setCount(10);
exceptionCountRule.setStatIntervalMs(60000);
exceptionCountRule.setTimeWindow(30);
rules.add(exceptionCountRule);
DegradeRuleManager.loadRules(rules);
}
熔断状态转换:Closed(正常)→ 触发条件满足 → Open(熔断中,所有请求被拒绝)→ 等待timeWindow秒 → Half-Open(半开,放行一个探测请求)→ 探测成功 → Closed;探测失败 → 重新Open。
自定义限流降级处理
被限流或熔断时,Sentinel默认抛出BlockException。生产环境需要自定义处理逻辑,返回友好提示而非500错误:
@Component
public class CustomBlockHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
BlockException e) throws Exception {
response.setStatus(429);
response.setContentType("application/json;charset=UTF-8");
Result<?> result;
if (e instanceof FlowException) {
result = Result.fail(429, "当前访问量过大,请稍后重试");
} else if (e instanceof DegradeException) {
result = Result.fail(503, "服务暂时不可用,已触发熔断保护");
} else if (e instanceof ParamFlowException) {
result = Result.fail(429, "该资源访问过于频繁");
} else if (e instanceof SystemBlockException) {
result = Result.fail(503, "系统负载过高,已触发保护");
} else {
result = Result.fail(429, "请求被拒绝");
}
response.getWriter().write(JSON.toJSONString(result));
}
}
Feign调用场景下,熔断降级需要单独处理。配置Feign集成Sentinel:
# application.yml
feign:
sentinel:
enabled: true
# Feign客户端配置fallback
@FeignClient(name = "user-service",
fallback = UserServiceFallback.class)
public interface UserServiceClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable Long id);
}
// 降级实现
@Component
public class UserServiceFallback implements UserServiceClient {
@Override
public User getUserById(Long id) {
// 返回默认用户或缓存数据
return User.defaultUser(id);
}
}
热点参数限流
某些场景下需要针对特定参数值进行限流,而非整个接口限流。比如秒杀场景,不同商品ID的限流阈值不同:
@PostConstruct
public void initParamRules() {
ParamFlowRule rule = new ParamFlowRule("/api/seckill")
.setParamIdx(0) // 限流参数索引(第0个参数)
.setGrade(RuleConstant.FLOW_GRADE_QPS)
.setCount(50); // 默认QPS 50
// 特定商品ID的限流阈值
ParamFlowItem item = new ParamFlowItem();
item.setObject("PRODUCT_001"); // 参数值
item.setClassType(String.class.getName());
item.setCount(10); // 该商品QPS 10(库存少)
rule.setParamFlowItemList(List.of(item));
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}
// Controller中使用@SentinelResource标记资源
@GetMapping("/seckill/{productId}")
@SentinelResource(value = "/api/seckill",
blockHandler = "seckillBlockHandler")
public Result seckill(@PathVariable String productId) {
return Result.success(orderService.seckill(productId));
}
// 限流后的处理方法
public Result seckillBlockHandler(String productId,
BlockException ex) {
return Result.fail(429, "商品" + productId + "秒杀排队中,请重试");
}
系统自适应限流
Sentinel的系统规则根据系统整体负载自动限流,不需要针对每个接口单独配置。四个维度:Load、CPU使用率、平均RT、入口QPS:
SystemRule rule = new SystemRule();
rule.setHighestSystemLoad(5.0); // Load1超过5触发
rule.setHighestCpuUsage(0.8); // CPU使用率超过80%
rule.setAvgRt(100); // 平均RT超过100ms
rule.setMaxThread(200); // 最大线程数200
SystemRuleManager.loadRules(Collections.singletonList(rule));
系统规则是兜底保护策略,优先级最高。当系统整体负载过高时,无论哪个接口触发,都会全面限制入口流量。这个机制防止个别接口异常导致整个节点不可用。
生产环境建议先观察一周的系统指标基线,再设置阈值。Load阈值设为CPU核心数的1.5倍,CPU使用率阈值设为80%,避免过早触发导致正常流量被拒。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-rong-duan-jiang-ji-sentinel-xian-liu/