这是 2022 年 2 月整理的微服务笔记。彼时微服务已是主流架构,服务间大量通过 HTTP(如 RestTemplate、OpenFeign)相互调用,限流容错(Sentinel、Hystrix、Resilience4j)是每个微服务应用都要面对的高可用话题。
级联故障(雪崩效应)
描述说明:由于一个或部分微服务故障,而导致其上层应用不可用,进而导致整个系统不可用。
服务 A ──调用──▶ 服务 B ──调用──▶ 服务 C(故障/变慢)
▲ ▲
│ │
└────── 线程耗尽,A 也不可用 ──────┘
B 等待 C 超时 → 线程池被占满 → A 的请求也在排队等待
→ A 资源耗尽 → 雪崩逐级向上蔓延 → 整个系统不可用
雪崩的成因:
- 服务变慢:下游服务响应慢,上游线程被阻塞等待。
- 线程耗尽:等待的请求占满线程池/连接池,新请求无法处理。
- 资源耗尽:CPU、内存、连接被耗尽,服务假死。
- 逐级蔓延:故障沿调用链向上传播,最终拖垮整个系统。
一句话:雪崩效应是服务故障的指数级放大——单个节点的失败,通过同步调用链路层层传导,最终导致全局不可用。
容错方案
超时
配置较小的超时,一旦超时即释放资源。
- 例如在配置 HTTP 连接池时设置小的超时时间(连接超时 + 读取超时)。
- 然后可以设置全局超时异常处理,统一处理超时异常。
// RestTemplate 设置连接超时与读取超时
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(1000); // 连接超时 1s
factory.setReadTimeout(2000); // 读取超时 2s
return new RestTemplate(factory);
}
思想:不让请求无限期等待,超时后快速释放线程资源。
舱壁隔离模式
为一个服务中不同的业务功能设置不同的线程池,独立分配资源。
服务 X
├── 线程池 A ──── 调用 支付服务
├── 线程池 B ──── 调用 订单服务
└── 线程池 C ──── 调用 库存服务
支付服务故障 → 线程池 A 被占满 → 只影响支付相关功能
订单、库存功能不受影响(各自独立的线程池)
思想:像轮船的隔舱一样,把不同业务隔离在独立的资源池中,让其中部分功能故障时不会无限使用整个系统资源。
断路器模式
实时监测应用,如果发现某个服务在一定时间内失败次数/失败率达到阈值,就打开断路器,直接 fail-fast 响应结果。
关闭 ──失败达到阈值──▶ 打开 ──超时后──▶ 半开 ──成功──▶ 关闭
▲ │ │
└────── 自我修复 ◀──────┘ └── 失败 ──▶ 打开
三个状态:
| 状态 | 行为 |
|---|---|
| 关闭(Closed) | 正常调用服务,统计失败率 |
| 打开(Open) | 直接 fail-fast,不再调用下游服务 |
| 半开(Half-Open) | 一段瞬间态,允许一次请求尝试调用服务 |
流程:
- 正常时断路器关闭,实时统计失败次数/失败率。
- 失败达到阈值 → 断路器打开,请求直接 fail-fast。
- 一段时间后,断路器进入半开(一个瞬间态),允许一次请求尝试调用服务。
- 成功 → 断路器关闭,应用恢复正常,实现自我修复。
- 不成功 → 继续保持打开,一段时间后再次进入半开。
思想:把”每次都去调用一个大概率失败的服务”变成”快速失败 + 定期试探”,保护系统资源不被无效调用耗尽。
三大容错方案互补:超时治标(快速释放资源)、舱壁隔离治局部(限制故障影响范围)、断路器治系统(全局熔断保护)。
几种限流的方法
令牌桶算法

令牌生成:令牌生成器以匀速的速率生成令牌到令牌桶中,令牌桶有容量限制,如果达到容量限制则丢弃生成的令牌。
令牌获取:每个请求的到来,需要先获取令牌,再执行后续逻辑。可选的是加入一个”缓冲队列”来暂存请求:
- 没能获取到令牌的请求暂时加入缓冲队列。
- 如果达到缓冲队列最大数量则丢弃新的请求。
- 如果令牌桶中有新的令牌产生,则从队列拿出请求处理。
令牌生成器 ──匀速生成──▶ 令牌桶(有容量上限,满了丢弃新令牌)
▲
│ 获取令牌
访问请求 ──▶ 缓冲队列(可选,满则丢弃)──▶ 处理请求
特点:令牌桶以恒定速率生产令牌,但处理请求不匀速——能处理突发流量,突发时效率更高,但会导致后台服务压力增大。
漏桶算法

漏桶的桶容器,保存的是请求。当桶满后,丢弃新的请求。漏桶以恒定的速率将请求向外漏出。
访问请求 ──▶ 漏桶(容量有限,满了丢弃新请求)
│
▼ 恒定速度漏出
访问请求(匀速流出)
特点:漏桶以恒定速率处理请求,对后台服务输出访问速度也恒定——彻底平滑流量,即使有突发也无法加速处理。
令牌桶 vs 漏桶
| 对比项 | 令牌桶 | 漏桶 |
|---|---|---|
| 容器保存的内容 | 令牌 | 请求 |
| 生产/处理速率 | 恒定速率生产令牌,处理请求不匀速 | 处理请求速率恒定 |
| 突发流量 | 可缓冲突发,效率更高 | 完全平滑,输出恒定 |
| 后台压力 | 突发时后台压力增大 | 后台输出速度恒定,压力可控 |
| 典型代表 | Guava RateLimiter | 常见限流中间件 |
核心区别:
- 令牌桶容器保存的是令牌,而漏桶容器保存的是请求。
- 令牌桶以恒定的速率生产令牌,处理请求不匀速,处理突发流量增大的效率更高,但会导致后台服务压力增大。
- 漏桶以恒定速率处理请求,对后台服务输出访问速度也恒定。
滑动窗口

我们假设窗口时间为 5 秒,它会随着时间推移向后滑动:
- 将窗口内的时间划分为五个小格子,每个格子代表 1 秒。
- 每个格子包含一个计数器,用来计算在当前时间内的访问请求数量。
- 时间窗口内的总访问量 = 所有格子计数器累加后的数值。
- 窗口向右滑动,最左边的格子滑出窗口,最右边的格子滑入。
时间(秒): 00:00 00:01 00:02 00:03 00:04 00:05 00:06
格子计数: 3 2 5 1 4 ← 当前窗口内:3+2+5+1+4=15
└──────── 窗口(5秒)────────┘
向右滑动 ──▶
特点:当时间窗口的跨度越长时,限流效果就越平滑。
滑动窗口是固定窗口(计数器)的改进版:固定窗口在窗口边界会出现”双倍流量”问题(前一个窗口末尾 + 后一个窗口开头各放行一批),滑动窗口通过细分格子、平滑滑动消除这个毛刺。
小结
- 雪崩效应:单个服务故障通过调用链逐级放大,最终拖垮整个系统。
- 三种容错方案:超时(快速释放资源)、舱壁隔离(线程池独立,限制故障影响范围)、断路器(失败阈值熔断 + 半开自我修复)。
- 令牌桶:匀速产令牌、可突发;容器存令牌。
- 漏桶:匀速处理请求、完全平滑;容器存请求。
- 滑动窗口:把窗口细分成格子平滑滑动,跨度越长越平滑;解决固定窗口的边界毛刺问题。