使用 KV 进行速率限制的诱惑是可以理解的。您已经在绑定中拥有了 KV,它是全局的,并且速率限制看起来很简单:为每个 IP 密钥增加一个计数器,并在超过限制时拒绝它。问题是这个实现不起作用——而且这个缺陷还不够微妙,不足以在测试中显现出来。
KV没有原子操作。没有比较和交换,没有原子增量。当两个 Worker 同时为同一个 IP 运行时,它们都会从计数器中获得 2% — 比如说,得到值 5 — — 他们都会从计数器中得到 3% 并得到值 6,并且其中一个增量会丢失。在真实流量下,增量丢失率随着竞争的增加而增加。速率限制器的计数少于应有的计数,并且应阻止的请求会通过。
这不是您可以通过重试解决的实现错误。这是 KV 中缺少同步原语的直接后果。该架构是为另一种类型的工作负载而设计的。
为什么速率限制需要原子性
速率限制计数器需要保证读-增量-写序列是原子的。如果两个进程在同一个计数器上同时执行此序列,则正确的结果是原始值加二。如果没有原子性,结果往往是原始值加一。
Cloudflare 针对这个问题有两种解决方案。第一个是本机速率限制功能,可通过仪表板中的规则或通过规则集 API 进行配置,该功能在工作人员级别以下运行,并使用具有正确同步保证的内部基础设施。第二个是持久对象,它提供了具有持久状态和序列化访问的隔离 - 您可以实现精确的计数器,因为一次只有一个 Worker 在该键的持久对象内运行。
KV 不是精确速率限制解决方案的一部分。尝试使用 KV 实施速率限制最终导致系统拒绝的流量少于应有的流量,而恰恰在速率限制最重要的高峰时段。
带 KV 的功能标记:什么有效,什么无效
功能标志是 KV 最常被引用的用例,它们在正确的限制内运行良好。基本模型很简单:您在包含所有系统标志的键中编写一个 JSON 对象,每个 Worker 读取该键来决定行为。
0
该模型之所以有效,是因为工作负载读取繁重,写入很少。旗帜每周更换几次。每个 PoP 中每个 Worker 的每个请求都会读取它。 KV 的读写比非常出色。
真正的限制是 60 秒的传播。启用标志并不会同时为所有用户激活它——存在一个窗口,其中部分 PoP 服务于旧行为,部分服务于新行为。对于大多数[功能标志4],这是可以忍受的。对于安全关键的部署,您需要一个标志来同时到达所有用户,这是一个真正的操作限制。
该模型的失败之处在于每个用户的动态标志评估。如果您需要根据用户属性(订阅计划、A/B 测试组、区域、特定实体)评估标志,则全局标志 JSON 不包含该逻辑。您需要对每个用户进行查找,这通常意味着调用 D1 或外部服务。 KV 仍然作为全局配置缓存,而不是作为完整的标志系统。
对于按用户百分比逐步推出(10% 查看该功能),使用 KV 实现需要您在 Worker 中编写采样逻辑,并仅使用 KV 来存储目标百分比。采样本身是无状态的——根据用户 ID 哈希在 Worker 中完成——因此 KV 被正确地用作配置存储。
分布式配置:KV 的最佳用例
如果功能标志是一个很好的用例,那么分布式配置就是理想的用例。区别在于变化的粒度和频率之一。
通过故意操作操作更改应用程序配置:外部服务端点更新、超时调整、允许的 IP 列表、业务参数。这些更改发生的频率非常低(更新之间间隔数小时或数天),并且需要针对每个请求进行读取。
在这种情况下,模块级缓存模式从 KV 中提取最多。只要隔离处于活动状态,Worker 模块范围内的变量就会持续存在,可能会处理数千个请求:
1
每个隔离株的第一个请求读取 KV。来自同一隔离区的所有后续请求都使用内存中的值 - 零 KV 延迟、零读取操作。当现有隔离区被运行时逐出时,配置更新会传播到新隔离区。
有效传播时间不再是全局 KV 传播的 60 秒,而是 60 秒加上活性隔离体的寿命。寿命长的分离株可以更长时间地保留旧配置。对于大多数操作更改来说,这是可以接受的。对于需要立即传播的紧急情况,您可以通过 API 强制重启 Workers。
每种场景下的运营成本
对于这三个用例,KV 成本模型会产生不同的压力。速率限制将是按请求写入——这对于任何卷都是不可行的。功能标志具有最小的写入成本和读取成本,这取决于每秒有多少个 PoP 正在读取密钥。通过模块级缓存,即使是数百万个请求读取的功能标志也可能消耗极少的读取操作——服务 10,000 个请求的隔离会读取一次 KV。
具有模块级缓存的分布式配置是 KV 中运营成本可能最低的场景。每次配置更改一次写入,每次重启每次隔离一次读取 - 即使在高流量应用程序中,这种模式的 KV 操作的每月成本也可以忽略不计。
这三个案例揭示了关于KV的什么信息
对速率限制、功能标志和分布式配置的分析明确了 KV 的边界:很少写入而经常读取的数据,其中最多 60 秒的不一致窗口是可以容忍的,并且不需要原子性。
当这三个条件中的任何一个不满足时,KV 将产生静默错误(无原子性),或不可接受的不一致(传播窗口),或令人望而却步的成本(高写入频率)。在实施之前认识到这个边界可以节省调试会话,这通常是了解工具不适用的情况的最昂贵的方法。
另请阅读
- [Cloudflare KV:当您需要编写时,全球分布式意味着什么5
- 【KV中的缓存失效:没人优雅解决的问题6
- [生产中的 KV:有效的模式和一开始就具有误导性的模式7
- [WAF + 速率限制 + 机器人管理:边缘8 保护三重奏
- [只有工人才能做的事情,只有页面才能做的事情以及两者相遇的地方99
- [KV、R2 与 Cache API:何时使用每个 Cloudflare10 存储层
