限流:概念、算法与分布式挑战

发布: (2026年2月8日 GMT+8 19:15)
12 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的正文内容,我将为您翻译成简体中文并保留原有的格式、Markdown 语法以及技术术语。谢谢!

介绍

如果你曾经构建过 API 或后端服务,你可能已经遇到过以下问题:

  • 某个用户发送了过多请求
  • 机器人滥用你的接口
  • 流量突增导致服务崩溃
  • 重试或定时任务意外地给系统带来过载

本文讨论的是 速率限制(rate limiting),这是一种简单却关键的技术,用于保护系统免受上述问题的影响。

在本篇文章中,我们将:

  • 了解为何需要速率限制
  • 学习常见的速率限制算法是如何工作的
  • 看看速率限制在实际系统中的定位

你不需要事先了解速率限制的相关知识。如果你了解基本的 API 和请求,就能跟上本文的内容。

目录

Source:

限流要解决的问题

当请求到达服务器时,它会消耗 CPU 时间、内存、数据库连接和网络带宽等资源。正常使用时这没问题,但当 大量请求同时到达 时,问题就会出现。

常见原因

  • 单个用户在紧密循环中发送请求
  • 机器人访问公开接口
  • 没有适当退避的重试机制
  • 发布或推广后流量突增

服务器把所有请求视为相同,它不知道哪个请求重要,哪个请求有害。

为什么会成为严重问题

如果请求量不断增长且没有限制:

  • 响应时间上升
  • 数据库开始变慢
  • 超时增多
  • 错误率激增
  • 最终服务对所有人不可用

为什么不能仅仅“扩容服务器”

我们的常见反应是 “再加几台服务器”。扩容有帮助,但 并不能解决根本问题

  • 无限的请求最终会压垮任何系统
  • 扩容会增加成本
  • 数据库和第三方 API 可能无法同等扩容

如果一直只扩容,只是 延迟 失败的到来。

系统真正需要的

  • 一种控制请求速率的方式
  • 防止意外或恶意滥用的保护
  • 公平性,防止单个用户抢占资源导致其他用户被饿死

这正是 限流(rate limiting) 所要解决的问题。

实际上速率限制的作用

从本质上讲,速率限制控制 在给定时间段内允许执行操作的频率。最常见的这种操作是 API 请求。

速率限制通常表现为:

  • 每位用户每分钟允许 100 次请求
  • 每个 IP 每秒允许 10 次请求
  • 对敏感端点每秒允许 1 次请求

当达到限制时,系统 不会 处理进一步的请求,直至足够的时间过去。

超出限制时会发生什么

  • 请求被拒绝
  • 服务器立即响应
  • 为其他用户保留资源

在基于 HTTP 的系统中,这通常以 429 Too Many Requests 响应返回。提前拒绝可以避免不必要的工作,如数据库查询或外部 API 调用。

速率限制能够保证的内容

  • 公平使用 – 单个用户不能消耗本该供所有人共享的资源
  • 可预测的性能 – 即使在负载下系统仍保持响应
  • 受控的突发 – 某些算法允许短时间的突发,同时强制长期限制
  • 系统保护 – 及早遏制意外的错误或行为异常的客户端

速率限制 能做到的事情

  • 认证(它不验证用户身份)
  • 完整的安全解决方案
  • 替代适当的输入验证

它是一种 流量控制机制,而不是安全闸门。

常见限流算法

不同系统根据需求使用不同的限流算法。没有一种算法是普遍“最佳”的;每种算法在简洁性、准确性和灵活性之间都有不同的取舍。

固定窗口计数器

最简单的限流形式。

工作原理

  1. 将时间划分为固定窗口(例如 1 分钟、1 小时)。
  2. 对每个用户,系统为 当前窗口 保持一个计数器。
  3. 每个进入的请求都会使该计数器递增。
  4. 当计数器达到上限时,后续请求被拒绝。
  5. 窗口结束时,计数器重置为零。

示例

  • 上限: 每分钟 5 次请求
  • 窗口: 12:00 – 12:01

用户在 12:00:59 发送 5 次请求 → 全部被允许。
计数器在 12:01:00 重置 → 再发送 5 次请求也被允许。

这意味着用户实际上在 1 秒内发出了 10 次请求

Fixed‑window example 1

Fixed‑window example 2

为什么固定窗口会失效

  • 用户可以利用窗口边界进行攻击。
  • 流量会变得非常突发。
  • 后端服务可能突然收到巨大的流量峰值。
  • 系统在高负载下会变得不公平。

固定窗口何时可以接受

  • 极低流量的系统。
  • 内部工具。
  • 原型或演示。
  • 正确性不如简洁性重要的场景。

在大多数生产环境的 API 中,通常会避免使用固定窗口。

滑动窗口

滑动窗口算法通过查看 当前时间往前的 N 秒 来解决固定窗口的突发问题。

工作原理

  1. 系统始终查看当前时间往前的 N 秒。
  2. 每个请求都在这个滚动窗口内进行评估。
  3. 系统统计过去 N 秒内发生了多少请求。
  4. 如果计数超过上限,请求被拒绝。

示例

Limit: 100 requests per 60 seconds
  • 在任意时刻,系统检查过去 60 秒内有多少请求。
  • 这可以防止因窗口重置而导致的突发流量,因为请求会更均匀地分布在时间轴上。

Sliding window example 1

Sliding window example 2

优点

  • 请求分配更公平。
  • 流量峰值自然得到抑制。
  • 没有窗口边界的突发问题。

缺点

  • 系统需要存储请求的时间戳。
  • 随着流量增加,内存使用会提升。
  • 每个请求的计算量更大。

令牌桶

在生产环境中最常用的算法之一,因为它在严格限制和良好用户体验之间取得了平衡。

工作原理

  • 每个用户都有一个装有令牌的桶。
  • 令牌以固定速率加入桶中。
  • 每个请求消耗一个令牌。
  • 如果没有令牌可用,请求被拒绝。
  • 桶有最大容量,令牌数量不会无限增长。

示例

Bucket size: 10 tokens
Refill rate: 1 token per second
场景结果
用户空闲 → 桶填满至 10 个令牌可进行一次突发请求
用户瞬间发送 10 次请求全部被允许
第 11 次请求被拒绝
1 秒后,加入 1 个令牌允许 1 次请求

Token bucket example 1

Token bucket example 2

Source: ds.s3.amazonaws.com/uploads/articles/umlnz48uisqgwkyl5e9o.jpg)

为什么它表现良好

  • 允许短时间的突发请求而不会破坏系统。
  • 强制执行长期的速率限制。
  • 提供更好的用户体验。
  • 实现简单且高效。

正因为这些特性,Token Bucket 通常是 API 的 默认选择

漏桶(Leaky Bucket)

侧重于产生 平滑且稳定的输出速率

工作原理

  • 进入的请求被放入队列(即桶)中。
  • 请求以恒定、固定的速率从队列中离开。
  • 如果队列已满,新的请求将被丢弃。

示例

  1. 大量请求一次性到达。
  2. 系统以稳定的速度处理请求。
  3. 当队列满时,多余的请求被丢弃。

优点

  • 能很好地保护下游系统。
  • 确保可预测的处理速度。
  • 防止突发流量冲击。

对面向用户的 API 的缺点

  • 突发请求会被延迟或丢弃。
  • 负载下延迟会增加。
  • 用户体验可能受影响。

Leaky bucket illustration

漏桶更适合用于后台任务和流水线,而不是交互式 API。

Comparing the Algorithms

The goal here is to understand which algorithm fits which situation.

High‑level Comparison

AlgorithmBurst handlingFairnessComplexityCommon usage
Fixed Window非常低简单或低流量系统
Sliding Window需要精确的系统
Token Bucket优秀中等大多数公共 API 的默认选择
Leaky Bucket优秀(输出)中等中等后台任务、流水线

Rate‑Limiting Algorithms Overview

AlgorithmAccuracyBurst CapacityComplexityTypical Use‑case
Token Bucket非常好中等大多数 API
Leaky Bucket中等中等后台任务

选择最符合你的流量模式、公平性要求和运营约束的算法。

分布式系统中的挑战

到目前为止,我们讨论的所有内容都假设只有一台服务器。在实际应用中,情况很少是这样——大多数系统都在 多台服务器 上运行,并通过负载均衡器进行调度。这会带来若干重要的挑战。

0 浏览
Back to Blog

相关文章

阅读更多 »