背景
Echo Nest 有一个速率受限的 API。给定的应用程序(使用 API 密钥在请求中标识)每分钟最多可以进行 120 次 REST 调用。服务响应包括对最后一分钟呼叫总数的估计;重复滥用 API(超过限制)可能会导致 API 密钥被撤销。
当从单台机器(向客户端提供服务的 Web 服务器)上使用时,很容易控制访问 - 服务器完全了解请求的历史并可以正确地自我调节。
但我正在开发一个程序,其中分布式独立客户端并行发出请求。
在这种情况下,最佳解决方案是什么就不太清楚了。总的来说,这个问题似乎是无法确定的——如果超过 120 个客户(都没有以前的历史记录)同时发出初始请求,那么速率将被超过。
但由于这是一个个人项目,预计客户使用是零星的(突发的),而且我的项目从未取得巨大成功,因此预计这不会是一个大问题。一个更可能的问题是,有时少数客户端希望尽快发出许多请求(例如,客户端可能需要在第一次启动时发出数千个请求 - 这是可能的两个客户端大约在同一时间启动,因此它们必须合作共享可用带宽)。
鉴于以上所有情况,什么是适合客户端的算法,以便他们适当地限制速率? 请注意,有限的合作是可能的,因为 API 返回所有客户端在最后一分钟的请求总数。
当前解决方案
我目前的解决方案(当问题被写出来时——给出了一个更好的方法作为答案)非常简单。每个客户端都有最后一次调用的时间和最后一分钟调用次数的记录,由 API 报告,在该调用上。
如果调用次数少于 60(限制的一半),则客户端不会限制。这允许少量请求的快速突发。
否则(即,当有更多先前的请求时)客户端计算它需要工作的限制速率(即period = 60 / (120 - number of previous requests)
),然后等待直到先前调用和当前时间之间的间隔超过该时间段(以秒为单位;60 秒分钟;每分钟最多 120 个请求)。这有效地限制了速率,因此,如果它单独行动,它不会超过限制。
但是上面有问题。如果您仔细考虑一下,您会发现对于大量请求,单个客户端会振荡并且不会达到最大吞吐量(这部分是因为“初始突发”会突然“落在窗口外”,部分是因为算法没有充分利用其历史)。并且多个客户会在一定程度上合作,但我怀疑它是最优的。
更好的解决方案
我可以想象一个更好的解决方案,它使用客户的完整本地历史记录并使用隐马尔可夫模型对其他客户进行建模。因此,每个客户端都将使用 API 报告来模拟其他(未知)客户端并相应地调整其速率。
我还可以想象一个用于单个客户端的算法,它逐渐从小突发的无限行为过渡到许多请求的最优、有限行为,而不会引入振荡。
这种方法存在吗?任何人都可以提供实现或参考吗?谁能想到更好的启发式方法?
我想这是某个地方的已知问题。在什么领域?排队论?
我还猜测(参见前面的评论)没有最佳解决方案,并且可能有一些知识/传统/公认的启发式在实践中效果很好。我很想知道什么......目前我正在努力找出已知网络协议中的类似问题(我想如果是这样的话,Perlman 会有一些漂亮的解决方案)。
我也对需要中央服务器来帮助协作的解决方案感兴趣(在较小程度上,以供将来参考,如果该程序变得流行)。
免责声明
这个问题根本不是对 Echo Nest 的批评;他们的服务和使用条件都很棒。但是我越想如何最好地使用它,它就会变得越复杂/有趣......
此外,每个客户端都有一个本地缓存,用于避免重复调用。