# 限流与背压原理

::: tip 前言
**双十一零点，几亿用户同时涌入——服务器扛得住吗？** 任何系统都有处理能力的上限。当请求量超过系统承载能力时，如果不加控制，结果就是所有人都用不了。限流和背压就是保护系统不被"压垮"的两道防线。
:::

**这篇文章会带你学什么？**

学完这章后，你将获得：

- **限流必要性**：理解为什么需要主动拒绝部分请求来保护系统
- **限流算法**：掌握令牌桶、漏桶、滑动窗口三种核心算法的原理和差异
- **背压机制**：理解当上游速度超过下游时的处理策略
- **多层限流**：了解从客户端到网关到服务的多层限流架构
- **实战能力**：知道在什么场景下选择什么限流策略

| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 为什么需要限流 | 雪崩效应、服务保护 |
| **第 2 章** | 限流算法 | 令牌桶、漏桶、滑动窗口 |
| **第 3 章** | 背压控制 | 缓冲区、丢弃策略、弹性扩容 |
| **第 4 章** | 多层限流架构 | 客户端、网关、服务端 |
| **第 5 章** | 实战与选型 | Nginx、Redis、Sentinel |

---

## 0. 全景图：拒绝"用户的动机

这听起来很反直觉——我们不是应该服务好每一个用户吗？但现实是：**不拒绝一部分请求，所有请求都会失败**。

想象一个只能坐 100 人的餐厅，突然涌进来 1000 人。如果不限流，结果不是 1000 人都能吃上饭，而是厨房崩溃、服务员瘫痪，1000 人谁都吃不上。正确的做法是在门口排队限流，让 100 人先进去，其余人等候。

::: tip 限流的核心目标
- **保护系统**：防止过载导致服务完全不可用
- **公平分配**：确保已接受的请求能正常处理
- **优雅降级**：被限流的请求收到明确的 429 状态码，而不是超时或 500 错误
:::

---

## 1. 限流算法：三种经典方案

限流的核心问题是：**在单位时间内，最多允许多少个请求通过？** 不同的算法在精确度、突发流量处理、实现复杂度上各有取舍。

<RateLimitAlgorithmDemo />

| 算法 | 原理 | 突发流量 | 精确度 | 实现复杂度 |
|------|------|---------|--------|-----------|
| 令牌桶 | 固定速率放令牌，请求消耗令牌 | 允许（桶中有存量） | 高 | 中 |
| 漏桶 | 请求排队，固定速率处理 | 不允许（完全平滑） | 高 | 中 |
| 滑动窗口 | 统计窗口内请求数 | 部分允许 | 较高 | 低 |
| 固定窗口 | 按时间窗口计数 | 边界处可能突发 | 低 | 最低 |

::: tip 选哪个算法？
- **API 限流**：令牌桶最常用，允许合理的突发流量
- **流量整形**：漏桶适合需要恒定输出速率的场景
- **简单计数**：滑动窗口实现简单，适合大多数 Web 应用
:::

---

## 2. 背压控制：当上游比下游快

限流解决的是"外部请求太多"的问题，而**背压（Backpressure）**解决的是"内部组件速度不匹配"的问题。

当生产者产生数据的速度持续超过消费者处理数据的速度时，中间的缓冲区会不断膨胀，最终导致内存溢出或数据丢失。背压机制就是让消费者能够"反向通知"生产者减速。

<BackpressureDemo />

::: tip 背压的四种策略
1. **丢弃（Drop）**：缓冲区满时丢弃新数据或旧数据，适合实时性要求高但允许丢失的场景
2. **阻塞（Block）**：让生产者暂停，等消费者处理完再继续，适合数据不能丢失的场景
3. **采样（Sample）**：只处理部分数据，适合高频数据流
4. **弹性扩容（Scale）**：动态增加消费者数量，适合云原生环境
:::

---

## 3. 多层限流架构

生产环境中，限流不是在某一个点做就够了，而是需要**多层防护**，每一层解决不同粒度的问题。

| 层级 | 位置 | 限流粒度 | 工具 |
|------|------|---------|------|
| 客户端 | 前端/App | 按钮防抖、请求节流 | lodash.throttle、debounce |
| CDN/WAF | 边缘节点 | IP 级别、地域级别 | Cloudflare Rate Limiting |
| API 网关 | 入口网关 | 路由级别、用户级别 | Nginx limit_req、Kong |
| 服务端 | 应用内部 | 接口级别、资源级别 | Sentinel、Resilience4j |
| 数据库 | 存储层 | 连接数、QPS | 连接池配置、慢查询熔断 |

::: tip 限流的 HTTP 规范
被限流的请求应该返回 `429 Too Many Requests` 状态码，并在响应头中包含：
- `Retry-After`: 建议客户端多久后重试（秒数或日期）
- `X-RateLimit-Limit`: 限流上限
- `X-RateLimit-Remaining`: 剩余配额
- `X-RateLimit-Reset`: 配额重置时间
:::

---

## 4. 实战选型

| 场景 | 推荐方案 | 说明 |
|------|---------|------|
| Nginx 入口限流 | `limit_req_zone` | 基于漏桶算法，配置简单 |
| 分布式限流 | Redis + Lua 脚本 | 令牌桶或滑动窗口，多实例共享计数 |
| Java 微服务 | Sentinel / Resilience4j | 支持熔断、降级、热点限流 |
| Node.js API | express-rate-limit | 简单易用，支持 Redis 存储 |
| Go 服务 | golang.org/x/time/rate | 标准库令牌桶实现 |

---

## 总结

限流和背压是保护系统稳定性的两道关键防线。限流控制外部流量的涌入速度，背压协调内部组件的处理速度。

回顾本章的关键要点：

1. **限流的必要性**：不拒绝部分请求，所有请求都会失败
2. **三种核心算法**：令牌桶（允许突发）、漏桶（完全平滑）、滑动窗口（简单精确）
3. **背压机制**：丢弃、阻塞、采样、扩容四种策略
4. **多层防护**：从客户端到数据库，每层解决不同粒度的问题
5. **429 规范**：被限流时返回标准状态码和限流头信息

## 延伸阅读

- [Stripe 的限流实践](https://stripe.com/blog/rate-limiters) - 支付系统的限流设计
- [Nginx limit_req 文档](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html) - Nginx 限流模块
- [Alibaba Sentinel](https://sentinelguard.io/) - 面向分布式服务的流量控制组件
- [Resilience4j](https://resilience4j.readme.io/) - Java 轻量级容错库
- [Token Bucket 算法详解](https://en.wikipedia.org/wiki/Token_bucket) - 令牌桶算法的数学原理
