5-3 熔断限流
概念解析
核心概念
| 概念 | 说明 |
|---|---|
| 熔断 | 快速失败,防止故障扩散 |
| 限流 | 控制请求速率,保护系统 |
| 降级 | 服务不可用时返回兜底数据 |
| 隔离 | 资源隔离,防止雪崩 |
限流算法
| 算法 | 特点 | 适用场景 |
|---|---|---|
| 计数器 | 简单,实现容易 | 固定限流 |
| 滑动窗口 | 精度高 | 精细控制 |
| 漏桶 | 流量整形 | 流量均匀 |
| 令牌桶 | 突发流量 | 允许突发 |
代码示例
1. Sentinel 限流
pom.xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.6</version>
</dependency>
<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:
server-addr: localhost:8718 # Sentinel 控制台端口
transport:
dashboard: localhost:8080
eager:
enabled: true # 启动即注册
注解方式
@RestController
public class SentinelController {
@GetMapping("/api/resource")
@SentinelResource(value = "apiResource",
blockHandler = "handleBlock",
fallback = "handleFallback")
public Result<String> getResource() {
return Result.success("Success");
}
// 限流处理
public Result<String> handleBlock(BlockException e) {
return Result.error(429, "请求过于频繁,请稍后重试");
}
// 降级处理
public Result<String> handleFallback(Throwable e) {
return Result.error("服务繁忙,请稍后重试");
}
}
2. Sentinel 规则配置
@Configuration
public class SentinelRuleConfig {
@PostConstruct
public void initRules() {
// 定义限流规则
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("apiResource");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100); // QPS 100
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
rules.add(rule);
FlowRuleManager.loadRules(rules);
// 定义熔断规则
List<CircuitBreakerRule> breakerRules = new ArrayList<>();
CircuitBreakerRule breakerRule = new CircuitBreakerRule();
breakerRule.setResource("apiResource");
breakerRule.setGrade(CircuitBreakerRule.SLOW_RATIO_RULE);
breakerRule.setCount(10); // 慢调用比例阈值
breakerRule.setSlowRatioThreshold(0.5); // 50% 慢调用
breakerRule.setStatIntervalMs(10000); // 统计窗口 10s
breakerRule.setMinRequestAmount(5); // 最小请求数
breakerRule.setRecoveryTimeoutMs(30_000); // 熔断时长
breakerRules.add(breakerRule);
CircuitBreakerRuleManager.loadRules(breakerRules);
}
}
3. Sentinel + OpenFeign
feign:
sentinel:
enabled: true # 启用 Sentinel
// 定义降级类
@Component
public class UserServiceFallback implements UserClient {
@Override
public Result<User> getUser(Long id) {
return Result.error("用户服务暂时不可用");
}
}
// 使用
@FeignClient(
name = "user-service",
fallback = UserServiceFallback.class
)
public interface UserClient { }
4. 自定义限流注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimiter {
int value() default 100; // 限流阈值
int timeout() default 0; // 等待超时
}
// 切面实现
@Aspect
@Component
public class RateLimiterAspect {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Around("@annotation(rateLimiter)")
public Object around(ProceedingJoinPoint joinPoint,
RateLimiter rateLimiter) throws Throwable {
String key = getKey(joinPoint);
int limit = rateLimiter.value();
// 令牌桶算法
String script = """
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or 0)
if current < limit then
redis.call('incr', key)
return 1
else
return 0
end
""";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
List.of(key),
String.valueOf(limit)
);
if (result == 0) {
throw new BizException(429, "请求过于频繁");
}
return joinPoint.proceed();
}
}
// 使用
@Service
public class OrderService {
@RateLimiter(value = 100, timeout = 0)
public void createOrder(Order order) {
// 创建订单
}
}
5. Resilience4j 熔断
pom.xml
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.2.0</version>
</dependency>
配置
resilience4j:
circuitbreaker:
instances:
userService:
registerHealthIndicator: true
slidingWindowSize: 10
minimumNumberOfCalls: 5
permittedNumberOfCallsInHalfOpenState: 3
automaticTransitionFromOpenToHalfOpenEnabled: true
waitDurationInOpenState: 30s
failureRateThreshold: 50
eventConsumerBufferSize: 10
timelimiter:
instances:
userService:
timeoutDuration: 3s
cancelRunningFuture: true
使用
@Service
public class UserService {
private final RestTemplate restTemplate;
@CircuitBreaker(name = "userService", fallbackMethod = "fallback")
@TimeLimiter(name = "userService")
public CompletableFuture<User> getUser(Long id) {
return CompletableFuture.supplyAsync(() ->
restTemplate.getForObject(
"http://user-service/api/users/" + id,
User.class
)
);
}
// 降级方法
public CompletableFuture<User> fallback(Long id, Throwable t) {
return CompletableFuture.completedFuture(
new User(0L, "降级用户", "default@example.com")
);
}
}
常见坑点
⚠️ 坑 1:Sentinel 规则不生效
// ❌ 规则配置类未加载
@Configuration
public class SentinelConfig {
@PostConstruct
public void initRules() {
// 规则加载代码
}
}
// ✅ 确保被扫描到
@SpringBootApplication
@ComponentScan(basePackages = {"com.example.demo", "com.example.other"})
// ✅ 或手动触发加载
System.setProperty("csp.sentinel.log.dir", "/tmp/logs");
⚠️ 坑 2:限流误杀
# 调整滑动窗口大小
resilience4j:
circuitbreaker:
instances:
backendA:
slidingWindowSize: 20 # 增大窗口
minimumNumberOfCalls: 10 # 增大最小调用数
⚠️ 坑 3:熔断后服务恢复慢
# 调整熔断恢复参数
resilience4j:
circuitbreaker:
instances:
backendA:
waitDurationInOpenState: 10s # 缩短熔断时长
permittedNumberOfCallsInHalfOpenState: 3 # 增加半开探测数
面试题
Q1:Sentinel 和 Hystrix 的区别?
参考答案:
| 维度 | Sentinel | Hystrix |
|---|---|---|
| 隔离策略 | 信号量隔离 | 线程池隔离 |
| 熔断策略 | 慢调用/异常比例 | 异常数/超时 |
| 实时指标 | QPS/响应时间 | 线程池指标 |
| 规则配置 | 动态推送 | 配置不可变 |
| Dashboard | 功能完善 | 功能有限 |
| 社区活跃 | 活跃 | 停止维护 |
Q2:什么是服务雪崩?如何避免?
参考答案:
服务雪崩:服务调用链中,一个服务故障导致整个链路不可用
解决方案:
| 方案 | 说明 |
|---|---|
| 熔断器 | 快速失败,阻止故障扩散 |
| 限流 | 控制请求速率 |
| 降级 | 返回兜底数据 |
| 隔离 | 资源隔离(线程池/信号量) |
| 超时控制 | 设置合理超时时间 |
// 超时配置
feign:
client:
config:
default:
connect-timeout: 5000
read-timeout: 10000
Q3:限流算法原理?
参考答案:
计数器:固定时间窗口内计数,超过阈值拒绝
- 简单,但可能有临界问题
滑动窗口:将窗口划分为更小的桶,更精确
- Redis ZSet 实现滑动窗口计数
漏桶:以固定速率消费请求
# 漏桶算法伪代码
bucket = []
rate = 10 # 每秒处理10个
while True:
if len(bucket) < capacity:
bucket.append(request)
process_bucket() # 按固定速率处理
令牌桶:以固定速率生成令牌,拿令牌处理
# 令牌桶算法伪代码
tokens = capacity
rate = 10 # 每秒生成10个令牌
while True:
if tokens > 0:
token = get_token()
tokens -= 1
process(token)
else:
wait()