Ramp 调用数十个第三方 API(Finicity、Teller、Zendesk、Salesforce),每个 API 都有自己的配额;它会在全部用户范围内运行周期性 Celery 任务;它还希望按已认证用户而非按 IP 对入站流量限流。这三种需求都指向同一个缺失的组件:一个在所有工作进程和服务器之间都准确、而不只是在单个进程内准确的限流器。

Ramp 原有的做法依赖 Celery 内置的按任务限流。该限制按工作进程生效,而非全局生效,因此要实施真正的系统级速率,工程师必须知道队列的工作进程数和自动扩缩行为,并手动把目标限制分摊到各工作进程——而一旦工作进程数变化,这个数字就过时了。在某些 Celery 预取配置下,被限流的任务甚至可能阻止工作进程领取新任务,导致整个队列停滞。

Ramp 评估了固定窗口、滑动窗口和漏桶算法,最终选择了通用信元速率算法(GCRA)——一种漏桶变体,每个键只需存储一个计算出的时间戳,而无需存储完整的请求日志。他们基于 Redis 实现该算法,加入分布式锁以弥补参考实现中的竞态条件缺口,并使用 Redis 的服务器时间而非各应用服务器的时钟,以避免机器之间的时钟漂移。

最终成果是一个统一的限流框架,Ramp 可以将其用于全部三种场景——第三方 API 调用、全局 Celery 任务节流和按用户的入站 API 限制——而工程师完全无需考虑工作进程数。

一个由 Redis 支撑的全局 GCRA 限流器

每个键只存储一个 TAT 值,远比滑动窗口的完整请求日志便宜,因此即使在 Ramp 的请求量下,限流器依然保持快速。

从 Redis 本身而非处理请求的某台应用服务器读取当前时间,消除了时钟漂移这一导致执行不一致的来源。

在检查并设置操作周围加 Redis 锁,弥补了两个并发请求可能都读到过期的 TAT 并都被放行的竞态条件。

构建一个通用框架而非一次性限流器,意味着同一套代码可以服务于第三方配额、Celery 任务节流和按用户 API 限制,而无需重复逻辑。

仅在进程内执行的限流只是对全局限制的近似;把计数器集中到共享的、可加锁的存储中,才能让这个数字真正名副其实。

Ramp 将基于 Redis 的 GCRA 限流器推广到文中描述的三种场景——全局第三方 API 配额、Celery 任务节流和入站按用户 API 限制——并配有单元测试和人工验证;Ramp 表示,随着更多使用场景出现,计划将同一框架扩展到新的限流算法。

从这里,追溯依据

参考来源

  1. Rate limiting with Redis Ramp Engineering (Builders Blog) · builders.ramp.com ·