Skip to content

第 4 章:设计限流器

引言

本章探讨限流器的设计与实现。限流器用于控制客户端或服务发送的流量速率,对于防止滥用、降低成本和保持服务器资源稳定至关重要。例如限制发帖、创建账号和领取奖励的频率。

限流的好处

  • 防止 DoS 攻击: 拦截过量调用,避免资源耗尽。
  • 降低成本: 限制不必要的请求,减少服务器开支。
  • 防止过载: 过滤过量请求,稳定服务器性能。

第 1 步:理解问题

主要功能

  • 在服务端对 API 限流。
  • 支持多条限流规则。
  • 适用于分布式环境中的大规模系统。
  • 可实现为独立服务或应用内代码。
  • 用户被限流时给予提示。

要求

  • 准确限制请求速率。
  • 尽量降低延迟。
  • 减少内存占用。
  • 支持分布式部署。
  • 清晰处理异常。
  • 具备高容错性。

第 2 步:高层设计

部署位置选择

<img src="/images/chapter-04/rate_limiter_architecture.png"  alt="限流中间件架构" width="550" />
  1. 客户端实现: 客户端可能被滥用,因此不可靠。
  2. 服务端实现: 控制力和可靠性较强,通常更合适。
  3. 中间件(API 网关): 便于集成限流的灵活选择。

部署位置的选择原则

  • 评估当前技术栈,选择高效方案。
  • 按业务需求选择合适算法。
  • 采用微服务架构时,可使用 API 网关。
  • 资源有限时,可选择商业产品。

第 3 步:限流算法

1. 令牌桶

令牌桶算法
  • 原理: 以固定速率向桶中加入令牌,每个请求消耗一个令牌。
  • 参数: 桶容量与令牌补充速率。
  • 优点: 容易实现、节省内存、允许突发流量。
  • 缺点: 需要仔细调整参数。

2. 漏桶

漏桶算法
  • 原理: 使用 FIFO 队列,以固定速率处理请求。

  • 优点: 节省内存、输出速率稳定。

  • 缺点: 突发流量可能导致后续请求延迟。

    示例:https://github.com/uber-go/ratelimit

3. 固定窗口计数器

固定窗口计数器
  • 原理: 将时间划分为固定区间,通过计数器限制请求数。

  • 优点: 简单,对特定场景效率高。

  • 缺点: 窗口边界处的流量突增可能突破限制。

  • 时间窗口边界处突然涌入的流量,可能使通过的请求数超过配额。

    固定窗口的问题

4. 滑动窗口日志

滑动窗口日志
  • 原理: 记录请求时间戳,以维护滚动时间窗口。
  • 优点: 限流准确。
  • 缺点: 内存消耗较高。

5. 滑动窗口计数器

滑动窗口计数器
  • 原理: 结合固定窗口与滑动日志的方法,平滑流量峰值。
  • 优点: 节省内存,能处理突发流量。
  • 缺点: 由于采用近似计算,限制未必完全严格。

高层架构

架构
  • 数据存储: 使用内存缓存(如 Redis)快速操作计数器。
  • 流程:
    1. 客户端向中间件发送请求。
    2. 中间件检查 Redis 中的计数器。
    3. 根据限额处理或拒绝请求。

进阶注意事项

分布式环境

  • 挑战: 竞态条件与同步问题。
  • 解决方法: 使用锁、Redis Lua 脚本或有序集合;通过集中式数据存储进行同步。

性能优化

  • 采用多数据中心部署降低延迟。
  • 使用最终一致性模型处理同步。

监控

  • 定期分析数据,确认算法有效性,并按需要调整规则。