vault backup: 2026-08-08 19:01:04

This commit is contained in:
2026-08-08 19:01:04 +08:00
parent 1a4a83bc67
commit e2975eb86b
46 changed files with 9370 additions and 0 deletions
@@ -0,0 +1,188 @@
---
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 倍作为第一道防线,配合监控告警逐步调优。
## 关联笔记
- [[服务注册与发现]]
- [[负载均衡算法]]
- [[配置中心设计]]