189 lines
7.3 KiB
Markdown
189 lines
7.3 KiB
Markdown
|
|
---
|
|||
|
|
tags: [arch/microservice, rate-limiting, circuit-breaker, sentinel, resilience4j, token-bucket, leaky-bucket]
|
|||
|
|
create time: 2026-08-08 18:00
|
|||
|
|
update time: 2026-08-08 18:00
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 限流熔断降级
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
限流、熔断、降级是微服务稳定性的三道防线。限流保护系统不被过量请求打垮,熔断在下游故障时快速失败避免级联雪崩,降级在服务不可用时返回兜底数据。三者层层递进,构成完整的容错体系。
|
|||
|
|
|
|||
|
|
## 核心原理
|
|||
|
|
|
|||
|
|
### 限流算法对比
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph LR
|
|||
|
|
subgraph Counter["计数器限流"]
|
|||
|
|
A[请求到达] --> B{本窗口内计数 < 阈值?}
|
|||
|
|
B -- 是 --> C[允许通过]
|
|||
|
|
B -- 否 --> D[拒绝]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph TokenBucket["令牌桶"]
|
|||
|
|
E[请求到达] --> F{桶中有足够令牌?}
|
|||
|
|
F -- 是 --> G[消费令牌放行]
|
|||
|
|
F -- 否 --> H[拒绝/等待]
|
|||
|
|
I[匀速生成器] -->|"每秒 N 个"| J[(令牌桶)]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph LeakyBucket["漏桶"]
|
|||
|
|
K[请求到达] --> L[(漏桶)]
|
|||
|
|
L --> M[固定速率流出处理]
|
|||
|
|
L -->|"溢出"| N[拒绝]
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 算法 | 核心机制 | 突发能力 | 延迟特性 | 适用场景 |
|
|||
|
|
|------|---------|---------|---------|---------|
|
|||
|
|
| 滑动窗口计数器 | 时间窗内计数,超过阈值则拒绝 | 差(窗口切换时有"双倍突发") | 低开销 | 简单 API QPS 限制 |
|
|||
|
|
| 令牌桶 | 以恒定速率向桶中添加令牌,请求需消耗令牌后才能通过 | 强(桶满时可突发消费存量令牌) | 中 | CDN 加速、消息队列限速 |
|
|||
|
|
| 漏桶 | 请求进入桶中,以固定速率流出处理 | 无(强制匀速) | 高(有排队延迟) | 需要平滑流量的网关层 |
|
|||
|
|
|
|||
|
|
**滑动窗口的改进版——Leak Bucket vs Token Bucket:**
|
|||
|
|
- 令牌桶适合保护生产者:允许突发流量,但长期受限于平均速率。
|
|||
|
|
- 漏桶适合保护消费者:无论输入多猛,输出永远匀速,但延迟不确定。
|
|||
|
|
|
|||
|
|
> [!NOTE]
|
|||
|
|
> Redisson 的限流器使用令牌桶算法实现 `rate(10, RateInterval.SECONDS, RateLimitRate.SEGMENTED)`,支持分布式环境下跨节点的精确计数。
|
|||
|
|
|
|||
|
|
### 熔断三态转换
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
stateDiagram-v2
|
|||
|
|
[*] --> Closed: 初始状态
|
|||
|
|
Closed --> Open: 失败率超过阈值 (如 >50%)
|
|||
|
|
Open --> HalfOpen: 等待探测期超时 (如 30s)
|
|||
|
|
HalfOpen --> Closed: 探测请求全部成功
|
|||
|
|
HalfOpen --> Open: 探测请求仍有失败
|
|||
|
|
Open --> Closed: 直接硬切回 (应急手段)
|
|||
|
|
|
|||
|
|
state Closed {
|
|||
|
|
[*] --> Monitoring: 持续统计失败比例
|
|||
|
|
}
|
|||
|
|
state Open {
|
|||
|
|
[*] --> RejectAll: 所有请求直接拒绝
|
|||
|
|
}
|
|||
|
|
state HalfOpen {
|
|||
|
|
[*] --> ProbeLimited: 有限探针进入
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**各状态的详细行为:**
|
|||
|
|
|
|||
|
|
| 状态 | 请求处理方式 | 统计维度 | 自动恢复? |
|
|||
|
|
|------|------------|---------|----------|
|
|||
|
|
| Closed(关闭) | 正常转发 | 统计最近 N 秒的请求成功/失败率 | 当失败率低于阈值且连续 M 次成功 |
|
|||
|
|
| Open(打开) | 直接拒绝(FastFail),不调用下游 | 不统计(因为根本不发请求) | 等待 ProbeTimeout 进入 HalfOpen |
|
|||
|
|
| HalfOpen(半开) | 发出少量探测请求(通常 1~3 个) | 只统计这几次探测结果 | 全成功 → Closed;任一失败 → Open |
|
|||
|
|
|
|||
|
|
**半开探测策略:**
|
|||
|
|
- 探测请求数量通常是固定的(如 Sentinel 默认 1 个),而非渐进式增加(像 Hystrix 那样)。
|
|||
|
|
- 探测期间原有业务请求仍然被熔断拦截,避免探测还没结束就压力暴增。
|
|||
|
|
- 如果探测成功立即转入 Closed,否则重新进入 Open 并重置倒计时。
|
|||
|
|
|
|||
|
|
### Sentinel — 规则推送 + 指标统计
|
|||
|
|
|
|||
|
|
阿里巴巴开源的流量控制组件,核心特性:
|
|||
|
|
|
|||
|
|
1. **双链路指标采集**:滑动时间窗口 + 并发线程数两种维度。
|
|||
|
|
2. **规则动态推送**:支持本地文件、Apollo、Nacos 等多种数据源,规则变更无需重启。
|
|||
|
|
3. **多级限流粒度**:API 级别、方法级别、URL 级别、资源级别。
|
|||
|
|
4. **系统自适应保护**:根据 Load、CPU 使用率、总 RPS 等自动调整限流阈值。
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// Sentinel Go SDK - 规则定义
|
|||
|
|
import "github.com/alibaba/sentinel-golang/api"
|
|||
|
|
import "github.com/alibaba/sentinel-golang/core/slot"
|
|||
|
|
|
|||
|
|
rule := &flow.Rule{
|
|||
|
|
Resource: "user-query",
|
|||
|
|
ThresholdType: flow.QPS,
|
|||
|
|
Threshold: 100,
|
|||
|
|
ControlBehavior: flow.Reject,
|
|||
|
|
}
|
|||
|
|
api.AddFlowRules([]*flow.Rule{rule})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Resilience4j — 函数式 API
|
|||
|
|
|
|||
|
|
与 Spring Cloud CircuitBreaker 深度集成,采用装饰器模式:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
// Resilience4j 熔断器 + 重试组合
|
|||
|
|
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
|
|||
|
|
.failureRateThreshold(50)
|
|||
|
|
.waitDurationInOpenState(Duration.ofMillis(1000))
|
|||
|
|
.slidingWindowSize(10)
|
|||
|
|
.build();
|
|||
|
|
|
|||
|
|
io.github.resilience4j.circuitbreaker.CircuitBreaker cb =
|
|||
|
|
CircuitBreaker.of("user-service", config);
|
|||
|
|
|
|||
|
|
Retry retry = Retry.of("user-service", RetryConfig.custom()
|
|||
|
|
.maxAttempts(3)
|
|||
|
|
.waitDuration(Duration.ofMillis(100))
|
|||
|
|
.build());
|
|||
|
|
|
|||
|
|
Decorators.ofSupplier(() -> userService.getUser(id))
|
|||
|
|
.retry(retry)
|
|||
|
|
.circuitBreaker(cb)
|
|||
|
|
.fallback(e -> new User(-1, "default"))
|
|||
|
|
.get();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!TIP]
|
|||
|
|
> 面试常考点:Resilience4j 和 Hystrix 的核心区别?Hystrix 基于线程池隔离(重),Resilience4j 基于信号量隔离(轻),且两者都是函数式设计而非继承 Thread。
|
|||
|
|
|
|||
|
|
### 降级的兜底思路
|
|||
|
|
|
|||
|
|
| 降级方式 | 示例 | 适合场景 |
|
|||
|
|
|---------|------|---------|
|
|||
|
|
| 返回缓存 | 用户信息从 DB 查不到时返回 Redis 旧值 | 对实时性要求不高的读接口 |
|
|||
|
|
| 返回默认值 | 推荐接口超时返回热门商品列表 | 非核心业务流程 |
|
|||
|
|
| 折叠请求 | 多个相同请求合并为一个真实请求 | 热点 Key 防护 |
|
|||
|
|
| Mock 数据 | 开发环境/预发布时直接返回预设 JSON | CI/CD 阶段 |
|
|||
|
|
|
|||
|
|
## 代码示例
|
|||
|
|
|
|||
|
|
Go 中使用信号量实现简单的并发限流:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
type SemaphoreLimit struct {
|
|||
|
|
sem chan struct{}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewSemaphoreLimit(maxConcurrent int) *SemaphoreLimit {
|
|||
|
|
return &SemaphoreLimit{sem: make(chan struct{}, maxConcurrent)}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *SemaphoreLimit) Allow() bool {
|
|||
|
|
select {
|
|||
|
|
case s.sem <- struct{}{}:
|
|||
|
|
defer func() { <-s.sem }()
|
|||
|
|
return true // 放行
|
|||
|
|
default:
|
|||
|
|
return false // 拒绝
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 实践场景
|
|||
|
|
|
|||
|
|
**秋招高频问题:**
|
|||
|
|
|
|||
|
|
- "Sentinel 的滑动窗口和传统的固定窗口有什么区别?" — 固定窗口在边界处会漏算(上一个窗口的尾巴 + 下一个窗口的头同时超限);Sentinel 将窗口细分为更小的 tick,在每个 tick 内独立计数再聚合,精度由 tickSize 决定。
|
|||
|
|
- "为什么熔断要从 Open 到 HalfOpen 而不是直接回 Closed?" — HalfOpen 是一个安全阀:如果下游还在恢复中,直接进入 Closed 会让流量瞬间全部灌入,可能导致二次雪崩。有限的探测请求可以验证下游是否真的恢复了。
|
|||
|
|
- "令牌桶和漏桶选型建议?" — 网关层对外暴露 API 时用令牌桶(允许客户端合理突发);内部服务间调用时用漏桶(保护消费方不被打爆)。
|
|||
|
|
|
|||
|
|
> [!WARNING]
|
|||
|
|
> 不要把限流的阈值设得太激进。建议先观察线上 P99/RPS 基线,然后设为基线的 1.5~2 倍作为第一道防线,配合监控告警逐步调优。
|
|||
|
|
|
|||
|
|
## 关联笔记
|
|||
|
|
|
|||
|
|
- [[服务注册与发现]]
|
|||
|
|
- [[负载均衡算法]]
|
|||
|
|
- [[配置中心设计]]
|