分布式系统限流熔断实战:保护微服务稳定性
一、限流与熔断的核心概念
在分布式系统中,服务之间的依赖关系复杂,当某个下游服务出现问题时,可能会导致级联故障。限流和熔断是保障系统稳定性的两大核心手段。
1.1 限流(Rate Limiting)
限流是通过限制单位时间内的请求数量,防止服务被过多请求压垮。常见的限流策略包括:
- 计数器算法:固定时间窗口内统计请求数
- 滑动窗口算法:更精确的时间窗口统计
- 漏桶算法:匀速处理请求,平滑流量
- 令牌桶算法:允许一定程度的突发流量
1.2 熔断(Circuit Breaker)
熔断机制借鉴了电路熔断的原理,当服务调用失败率达到阈值时,自动切断调用链路,避免级联失败。熔断状态包括:
- 闭合状态:正常工作,统计失败率
- 打开状态:熔断触发,直接返回错误
- 半开状态:尝试恢复,检测服务是否可用
二、使用 Sentinel 实现限流
Sentinel 是阿里巴巴开源的流量控制组件,提供了丰富的限流策略。
2.1 引入依赖
xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.6</version>
</dependency>
2.2 配置限流规则
java
public class SentinelConfig {
@PostConstruct
public void init() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("order-service");
rule.setCount(100);
rule.setGrade(0);
rule.setLimitApp("default");
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}
2.3 注解方式使用限流
java
@Service
public class OrderService {
@SentinelResource(value = "order-service",
blockHandler = "handleBlock")
public Order createOrder(OrderRequest request) {
// 业务逻辑
return orderRepository.save(request);
}
public Order handleBlock(OrderRequest request, BlockException ex) {
log.warn("请求被限流: {}", ex.getMessage());
return Order.builder()
.status("FAILED")
.message("系统繁忙,请稍后重试")
.build();
}
}
三、使用 Resilience4j 实现熔断
Resilience4j 是一个轻量级的容错库,提供了熔断、限流、重试等功能。
3.1 引入依赖
xml
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-retry</artifactId>
<version>2.2.0</version>
</dependency>
3.2 配置熔断策略
yaml
resilience4j:
circuitbreaker:
instances:
payment-service:
registerHealthIndicator: true
slidingWindowSize: 100
minimumNumberOfCalls: 10
permittedNumberOfCallsInHalfOpenState: 3
automaticTransitionFromOpenToHalfOpenEnabled: true
waitDurationInOpenState: 10s
failureRateThreshold: 50
eventConsumerBufferSize: 10
3.3 编程式使用熔断
java
@Service
public class PaymentService {
private final CircuitBreaker circuitBreaker;
@Autowired
public PaymentService(CircuitBreakerRegistry registry) {
this.circuitBreaker = registry.circuitBreaker("payment-service");
}
public PaymentResponse processPayment(PaymentRequest request) {
Supplier<PaymentResponse> supplier = CircuitBreaker
.decorateSupplier(circuitBreaker, () -> {
return restTemplate.postForObject(
"http://payment-service/api/pay",
request,
PaymentResponse.class
);
});
return Try.ofSupplier(supplier)
.recover(ex -> {
log.error("支付服务熔断: {}", ex.getMessage());
return PaymentResponse.failure("支付服务暂时不可用");
})
.get();
}
}
四、网关层限流方案
在 API 网关层进行限流可以有效保护后端服务。
4.1 Spring Cloud Gateway 限流
java
@Configuration
public class GatewayConfig {
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("order-service", r -> r.path("/api/orders/**")
.filters(f -> f.requestRateLimiter()
.rateLimiter(redisRateLimiter())
.keyResolver(ipKeyResolver()))
.uri("lb://order-service"))
.build();
}
}
4.2 限流配置
yaml
spring:
cloud:
gateway:
filter:
request-rate-limiter:
redis-rate-limiter:
replenishRate: 100
burstCapacity: 200
五、分布式限流方案
在分布式环境下,单机限流无法满足全局限流需求。
5.1 Redis + Lua 实现分布式限流
lua
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call('GET', key)
if current and tonumber(current) >= limit then
return 0
end
current = redis.call('INCR', key)
if tonumber(current) == 1 then
redis.call('EXPIRE', key, window)
end
return 1
5.2 Java 调用示例
java
public class RedisRateLimiter {
private final StringRedisTemplate redisTemplate;
@Autowired
public RedisRateLimiter(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public boolean tryAcquire(String key, int limit, int windowSeconds) {
String luaScript = // Lua脚本内容
Long result = redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList(key),
String.valueOf(limit),
String.valueOf(windowSeconds)
);
return result != null && result == 1;
}
}
六、最佳实践总结
6.1 限流策略选择
| 场景 | 推荐策略 | 说明 |
|---|---|---|
| API入口 | 令牌桶算法 | 允许突发流量 |
| 资源保护 | 计数器算法 | 简单高效 |
| 数据库访问 | 漏桶算法 | 平滑流量 |
6.2 熔断配置建议
- failureRateThreshold: 建议设置为 50%
- waitDurationInOpenState: 建议设置为 10-30 秒
- slidingWindowSize: 根据 QPS 调整,建议 100-1000
6.3 多层防护策略
┌─────────────────────────────────────────────────────┐
│ API Gateway (入口限流) │
├─────────────────────────────────────────────────────┤
│ Service Mesh (熔断降级) │
├─────────────────────────────────────────────────────┤
│ 业务层 (本地限流) │
├─────────────────────────────────────────────────────┤
│ 数据库 (连接池限流) │
└─────────────────────────────────────────────────────┘
通过多层防护,可以有效保障分布式系统的稳定性,防止单点故障引发的级联崩溃。
七、监控与告警
限流熔断的效果需要通过监控来评估:
yaml
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
配置 Prometheus 告警规则:
yaml
groups:
- name: circuit_breaker_alerts
rules:
- alert: CircuitBreakerOpen
expr: resilience4j_circuitbreaker_state{state="OPEN"} == 1
for: 1m
labels:
severity: critical
annotations:
summary: "熔断器打开: {{ $labels.name }}"
description: "{{ $labels.name }} 熔断器已打开,服务可能不可用"
通过合理配置限流熔断策略,并结合监控告警,可以在保障系统稳定性的同时,为用户提供良好的服务体验。