跳至正文
来两杯美式
返回

微服务限流与容错:雪崩效应、熔断器与三种限流算法

By 来两杯美式
发布于

这是 2022 年 2 月整理的微服务笔记。彼时微服务已是主流架构,服务间大量通过 HTTP(如 RestTemplate、OpenFeign)相互调用,限流容错(Sentinel、Hystrix、Resilience4j)是每个微服务应用都要面对的高可用话题。

级联故障(雪崩效应)

描述说明:由于一个或部分微服务故障,而导致其上层应用不可用,进而导致整个系统不可用

服务 A ──调用──▶ 服务 B ──调用──▶ 服务 C(故障/变慢)
  ▲               ▲
  │               │
  └────── 线程耗尽,A 也不可用 ──────┘

B 等待 C 超时 → 线程池被占满 → A 的请求也在排队等待
→ A 资源耗尽 → 雪崩逐级向上蔓延 → 整个系统不可用

雪崩的成因

  1. 服务变慢:下游服务响应慢,上游线程被阻塞等待。
  2. 线程耗尽:等待的请求占满线程池/连接池,新请求无法处理。
  3. 资源耗尽:CPU、内存、连接被耗尽,服务假死。
  4. 逐级蔓延:故障沿调用链向上传播,最终拖垮整个系统。

一句话:雪崩效应是服务故障的指数级放大——单个节点的失败,通过同步调用链路层层传导,最终导致全局不可用。

容错方案

超时

配置较小的超时,一旦超时即释放资源

// 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)一段瞬间态,允许一次请求尝试调用服务

流程

  1. 正常时断路器关闭,实时统计失败次数/失败率。
  2. 失败达到阈值 → 断路器打开,请求直接 fail-fast。
  3. 一段时间后,断路器进入半开(一个瞬间态),允许一次请求尝试调用服务。
  4. 成功 → 断路器关闭,应用恢复正常,实现自我修复
  5. 不成功 → 继续保持打开,一段时间后再次进入半开。

思想:把”每次都去调用一个大概率失败的服务”变成”快速失败 + 定期试探”,保护系统资源不被无效调用耗尽。

三大容错方案互补:超时治标(快速释放资源)、舱壁隔离治局部(限制故障影响范围)、断路器治系统(全局熔断保护)。

几种限流的方法

令牌桶算法

令牌桶算法:匀速生成令牌,请求需先获取令牌才能执行

令牌生成:令牌生成器以匀速的速率生成令牌到令牌桶中,令牌桶有容量限制,如果达到容量限制则丢弃生成的令牌。

令牌获取:每个请求的到来,需要先获取令牌,再执行后续逻辑。可选的是加入一个”缓冲队列”来暂存请求:

令牌生成器 ──匀速生成──▶ 令牌桶(有容量上限,满了丢弃新令牌)

                                │ 获取令牌
访问请求 ──▶ 缓冲队列(可选,满则丢弃)──▶ 处理请求

特点:令牌桶以恒定速率生产令牌,但处理请求不匀速——能处理突发流量,突发时效率更高,但会导致后台服务压力增大。

漏桶算法

漏桶算法:请求进入漏桶,以恒定速率漏出

漏桶的桶容器,保存的是请求。当桶满后,丢弃新的请求。漏桶以恒定的速率将请求向外漏出。

访问请求 ──▶ 漏桶(容量有限,满了丢弃新请求)

                  ▼ 恒定速度漏出
             访问请求(匀速流出)

特点:漏桶以恒定速率处理请求,对后台服务输出访问速度也恒定——彻底平滑流量,即使有突发也无法加速处理。

令牌桶 vs 漏桶

对比项令牌桶漏桶
容器保存的内容令牌请求
生产/处理速率恒定速率生产令牌,处理请求不匀速处理请求速率恒定
突发流量缓冲突发,效率更高完全平滑,输出恒定
后台压力突发时后台压力增大后台输出速度恒定,压力可控
典型代表Guava RateLimiter常见限流中间件

核心区别

滑动窗口

滑动窗口限流:窗口随时间向后滑动,格子计数器累加

我们假设窗口时间为 5 秒,它会随着时间推移向后滑动:

  1. 将窗口内的时间划分为五个小格子,每个格子代表 1 秒。
  2. 每个格子包含一个计数器,用来计算在当前时间内的访问请求数量。
  3. 时间窗口内的总访问量 = 所有格子计数器累加后的数值
  4. 窗口向右滑动,最左边的格子滑出窗口,最右边的格子滑入。
时间(秒):  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秒)────────┘
                      向右滑动 ──▶

特点当时间窗口的跨度越长时,限流效果就越平滑

滑动窗口是固定窗口(计数器)的改进版:固定窗口在窗口边界会出现”双倍流量”问题(前一个窗口末尾 + 后一个窗口开头各放行一批),滑动窗口通过细分格子、平滑滑动消除这个毛刺。

小结


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
查看系列全部文章
  1. 01.CAP 与 BASE:分布式系统的理论基石
  2. 02.拜占庭将军问题:两忠一叛、口信与签名消息之解
  3. 03.ZooKeeper 原理与实践:数据模型、Watcher 与 ZAB 协议
  4. 04.分布式事务五种方案:消息驱动 / XA / TCC / 本地消息表 / Saga
  5. 05.微服务限流与容错:雪崩效应、熔断器与三种限流算法

上一篇
拜占庭将军问题:两忠一叛、口信与签名消息之解
下一篇
VitePress 动态渲染全栈落地方案