高并发场景下为什么必须做限流熔断
微服务架构中,一个服务的不可用会沿着调用链向上蔓延。用户服务响应慢 → 订单服务线程池耗尽 → 支付服务超时 → 整条链路雪崩。限流和熔断是两条防线:限流在入口处控制流量,防止系统过载;熔断在出口处快速失败,防止级联故障。两者配合才能在高并发场景下保住系统的核心可用性。
Sentinel限流接入与规则配置
Alibaba Sentinel是Spring Boot生态中应用最广的限流组件,支持QPS限流、线程数限流和熔断降级。Spring Boot接入只需两个依赖:
<!-- pom.xml -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2022.0.0.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.8.7</version>
</dependency>
application.yml基础配置:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
port: 8719
eager: true
web-context-unify: false
启动Sentinel Dashboard后,通过代码定义限流规则:
@Configuration
public class SentinelRuleConfig {
@PostConstruct
public void initRules() {
// 接口QPS限流规则
List<FlowRule> rules = new ArrayList<>();
FlowRule apiRule = new FlowRule();
apiRule.setResource("order-service:/api/orders");
apiRule.setLimitApp("default");
apiRule.setCount(500); // QPS阈值500
apiRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
apiRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
apiRule.setWarmUpPeriodSec(10); // 预热10秒
rules.add(apiRule);
// 线程数限流 - 防止线程池耗尽
FlowRule threadRule = new FlowRule();
threadRule.setResource("order-service:/api/orders");
threadRule.setLimitApp("default");
threadRule.setCount(200); // 最大并发线程200
threadRule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
rules.add(threadRule);
FlowRuleManager.loadRules(rules);
}
}
WARM_UP预热模式适合突发流量场景——冷启动时逐步放开流量,避免系统瞬间承压过大。QPS限流控制吞吐量,线程数限流保护并发容量,两种规则同时配置形成双重保护。
Sentinel熔断降级规则配置
熔断基于三个维度的统计:慢调用比例、异常比例、异常数。实际项目中推荐使用慢调用比例+异常比例的组合:
@PostConstruct
public void initDegradeRules() {
List<DegradeRule> rules = new ArrayList<>();
// 慢调用比例熔断
DegradeRule slowCallRule = new DegradeRule("payment-service:/api/pay");
slowCallRule.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType());
slowCallRule.setCount(1000); // 慢调用阈值1000ms
slowCallRule.setSlowRatioThreshold(0.6); // 慢调用比例60%触发熔断
slowCallRule.setTimeWindow(30); // 熔断持续30秒
slowCallRule.setMinRequestAmount(10); // 最小请求数10
slowCallRule.setStatIntervalMs(10000); // 统计窗口10秒
rules.add(slowCallRule);
// 异常比例熔断
DegradeRule errorRatioRule = new DegradeRule("payment-service:/api/pay");
errorRatioRule.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType());
errorRatioRule.setCount(0.5); // 异常比例50%触发熔断
errorRatioRule.setTimeWindow(30);
errorRatioRule.setMinRequestAmount(5);
errorRatioRule.setStatIntervalMs(10000);
rules.add(errorRatioRule);
DegradeRuleManager.loadRules(rules);
}
熔断器状态机:CLOSED(正常放行)→ OPEN(直接拒绝)→ HALF_OPEN(放行少量请求探测恢复)。timeWindow控制OPEN状态的持续时间,到期后自动进入HALF_OPEN探测。
降级兜底与BlockHandler实现
限流和熔断触发后,需要返回降级响应而非直接报错。使用@SentinelResource注解配置:
@Service
public class OrderService {
@SentinelResource(
value = "createOrder",
blockHandler = "createOrderBlockHandler",
fallback = "createOrderFallback"
)
public OrderResult createOrder(OrderRequest request) {
// 正常业务逻辑
return orderClient.create(request);
}
// 限流/熔断触发时的处理
public OrderResult createOrderBlockHandler(
OrderRequest request, BlockException ex
) {
log.warn("订单创建被限流: {}", ex.getRule());
return OrderResult.fail("系统繁忙,请稍后重试");
}
// 业务异常降级
public OrderResult createOrderFallback(
OrderRequest request, Throwable throwable
) {
log.error("订单创建异常: ", throwable);
return OrderResult.fail("服务暂时不可用");
}
}
blockHandler处理限流/熔断场景,fallback处理业务异常。两者的区别:blockHandler参数必须包含BlockException,fallback可以捕获任何异常。降级响应要返回有意义的业务信息,而不是通用错误码,让前端可以区分处理(如显示重试按钮或排队提示)。
分布式限流:集群模式下的统一流量控制
单机限流在多实例部署时无法精确控制全局QPS。Sentinel集群限流模式通过Token Server统一分配令牌:
// Token Server配置(独立部署)
@Configuration
public class ClusterServerConfig {
@Bean
public ClusterTokenServer clusterTokenServer() {
ClusterServerConfigManager.loadGlobalConfig();
// 全局QPS阈值
ClusterServerConfigManager.loadServerNamespaceSet(
Set.of("order-service")
);
ClusterServerConfigManager.loadServerFlowConfig(
"order-service", new ClusterFlowConfig()
.setSampleCount(10)
.setWindowIntervalMs(1000)
);
return new ClusterTokenServer();
}
}
// Token Client配置(每个业务实例)
@Configuration
public class ClusterClientConfig {
@PostConstruct
public void init() {
ClusterClientConfigManager.setTokenServerConfig(
new ClusterClientConfig()
.setServerHost("sentinel-token-server")
.setServerPort(8300)
.setRequestTimeout(200)
);
}
}
集群限流的代价是每次请求多一次网络调用(向Token Server申请令牌),延迟增加约1-2ms。对延迟极度敏感的场景可以退回单机限流(各实例分摊配额),或使用Guava RateLimiter做本地二级限流。
高并发场景下的线程池隔离配置
Sentinel默认信号量隔离(所有请求共享线程池),在依赖服务变慢时会拖垮整个应用的线程池。线程池隔离让不同资源使用独立线程池:
@Configuration
public class ThreadPoolIsolationConfig {
@Bean("orderThreadPool")
public ThreadPoolExecutor orderThreadPool() {
return new ThreadPoolExecutor(
50, // 核心线程数
200, // 最大线程数
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000),
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
@Bean("paymentThreadPool")
public ThreadPoolExecutor paymentThreadPool() {
return new ThreadPoolExecutor(
20, 100,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(500),
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
}
支付服务隔离到独立线程池,即使支付服务超时拖慢线程,也不会影响订单查询等其他接口。CallerRunsPolicy拒绝策略让提交线程自己执行任务,起到天然限流效果——当队列满后,调用方线程被阻塞,自动降低了上游的请求速率。
限流熔断配置的监控与调优
Sentinel Dashboard提供实时监控视图,但生产环境更推荐接入Prometheus:
# application.yml - 暴露metrics
management:
endpoints:
web:
exposure:
include: prometheus
metrics:
export:
prometheus:
enabled: true
# Prometheus抓取配置
scrape_configs:
- job_name: 'spring-sentinel'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['order-service:8080']
关键监控指标:sentinel_block_qps(被限流QPS)、sentinel_pass_qps(通过QPS)、sentinel_exception_count(异常计数)。设置告警规则:block_qps持续5分钟大于pass_qps的30%,说明限流阈值可能偏低需要调高;exception_count突增伴随RT上涨,说明下游服务可能异常需要检查。
限流值和熔断阈值没有万能公式,必须基于压测数据和线上监控逐步调整。压测时逐步加压到系统瓶颈,记录各QPS水位下的RT和错误率,限流值设在RT开始非线性上升的拐点之下。熔断阈值设在系统能容忍的最大异常比例,通常40%-60%。配置先保守再逐步放开,比一上来就设高值再降要安全得多。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-gao-bing-fa-she-ji-jie-kou-xian-liu-yu/