从 Android 的角度用Java构建一个简单的速率限制器:
从 Android 的角度使用
- 考虑这样一种情况,您正在编写代码来捕获用户的签名。当他们拖动指针时,您捕获了数千个点。平滑签名可能不需要所有这些,因此您可以使用速率限制进行采样。
- 某些事件调用频率很高。你可以控制它。
- 我们有MessageQueue空闲的监听器。当我们在主线程中监听它时,它会以一种随意的方式被调用。有时,它会在一秒钟内被调用几次。如果我们想构建一个心跳系统来告诉我们主线程何时空闲,我们可以使用它来接收每秒的事件。如果我们一秒钟都没有收到事件,我们可以假设主线程很忙。
- 对于框架/库的 API 配额管理,您可以根据用户选择的付款计划来控制 API 调用。
为了建立我们的速率限制器的核心,我们需要确保在任何两秒之间,我们不应该允许超过N个交易。我们将如何做到这一点?
考虑一下我们进行第一笔交易的时刻t0。所以。
直到(t0 + 1)s,我们只允许做N个交易。那么如何确保这一点呢?在进行下一次交易时,我们将检查是否
当前时间≤(t0+1)。如果不是,那就意味着我们已经进入了另一个秒,我们被允许进行N次交易。让我们看看一个小的代码部分来证明这一点。
long now = System.nanoTime(); |
详细点击标题见源码
速率限制
现实世界的用户是残酷的、不耐烦的和鬼鬼祟祟的。在高并发系统中,您的服务器可能会受到虚假请求的轰炸,因此您可能希望对其进行控制。
一些现实世界的用例可能如下:
1. API 配额管理——作为提供商,您可能希望根据用户采用的付款计划来限制向您的服务器发出 API 请求的速率。这可能在客户端或服务端。
2. 安全——防止 DDOS 攻击。
3. 成本控制——这不一定是针对服务端,甚至是客户端。如果某个组件以非常高的速率发出事件,它可以帮助控制它。它可以帮助控制从客户端发送的遥测数据。
我们在限速时的选择
根据我们正在处理的请求/事件的类型,可能会发生以下情况:
- 我们可以放弃额外的请求
- 我们可以选择让请求等待,直到系统将它们限制到预定义的速率。
常见的限速算法
- 令牌桶算法
- 漏桶算法
- 固定窗口
我们将以固定窗口算法为支点。让我们写下实施的高级要求。
固定窗口 ——窗口被预先分割,每个窗口都有一个计数器。每个请求都会将计数器加一。一旦计数器达到阈值,新请求将被丢弃,直到新的时间窗口开始。
要求
- 应该能够接受每秒或速率所需的 (TPS) 交易。
- 如果超过我们定义的速率,应该放弃交易。
- 应该在并发情况下工作。