14

我正在开发一个与 Web 服务 API 接口的 Python 库。像我遇到的许多 Web 服务一样,这一项请求限制了请求的速率。我想为limit类实例化提供一个可选参数,如果提供,它将保持传出请求,直到指定的秒数过去。

我了解一般情况如下:该类的实例通过方法发出请求。当它发生时,该方法会发出一些信号,在某处设置一个锁定变量,并开始一个倒计时计时器,以计算limit. (很可能,锁是倒数计时器本身。)如果在此时间范围内发出另一个请求,则必须排队,直到倒数计时器达到零并且锁被解除;此时,发送队列中最旧的请求,并重置倒数计时器并重新锁定。

这是线程的情况吗?还有另一种我没有看到的方法吗?

倒数计时器和锁应该是实例变量,还是应该属于类,以便类的所有实例都持有请求?

此外,在库中提供速率限制功能通常是一个坏主意吗?我的理由是,默认情况下,倒计时为零秒,该库仍然允许开发人员使用该库并提供他们自己的速率限制方案。但是,鉴于任何使用该服务的开发人员都需要对请求进行速率限制,但我认为库提供一种速率限制方法会很方便。

无论是否在库中放置速率限制方案,我都想使用该库编写一个应用程序,因此建议的技术将派上用场。

4

6 回答 6

13

除非需要,否则不要重新发明轮子。检查真棒图书馆ratelimit。如果您只是想出于任何原因限制对休息 api 的调用并继续您的生活,那就完美了。

from datetime import timedelta
from ratelimit import limits, sleep_and_retry
import requests

@sleep_and_retry
@limits(calls=1, period=timedelta(seconds=60).total_seconds())
def get_foobar():
    response = requests.get('https://httpbin.org/get')
    response.raise_for_status()
    return response.json()

如果发出的请求多于每分钟一个,这将阻塞线程。

于 2018-05-21T02:49:04.670 回答
11

这与队列和调度程序一起工作得更好。

您将处理分为两个方面:sourcedispatch。这些可以是单独的线程(或者如果更容易的话,可以是单独的进程)。

端以任何让他们满意的速度创建和排队请求。

Dispatch端执行此操作。

  1. 获取请求开始时间,s

  2. 将请求出列,通过远程服务处理请求。

  3. 获取当前时间t。睡眠速率- ( t - s ) 秒。

如果你想运行Source端直接连接到远程服务,你可以这样做,并绕过速率限制。这对于使用远程服务的模拟版本进行内部测试很有用。

这方面的困难部分是为您可以排队的每个请求创建一些表示。由于 Python Queue几乎可以处理任何事情,因此您不必做太多事情。

如果您使用多处理,则必须腌制您的对象以将它们放入管道中。

于 2008-12-30T20:00:33.837 回答
2

排队可能过于复杂。一个更简单的解决方案是为您的类提供最后一次调用服务的时间变量。每当调用服务 (!1) 时,将 waitTime 设置为delay - Now + lastcalltimedelay应该等于请求之间的最小允许时间。如果这个数字是正数,请在拨打电话之前睡很长时间(!2)。这种方法的缺点/优点在于它将 Web 服务请求视为同步的。优点是它非常简单且易于实现。

  • (!1):应该在收到服务的响应后立即在包装器内(可能在包装器的底部)发生。
  • (!2):应该在 web 服务周围的 python 包装器被调用时发生,在包装器的顶部。

S.Lott 的解决方案当然更优雅。

于 2008-12-30T20:23:16.913 回答
1

您的速率限制方案应该受到底层代码(同步或异步)的调用约定以及该速率限制将在什么范围(线程、进程、机器、集群?)上运行的严重影响。

我建议将所有变量保留在实例中,以便您可以轻松实现多个周期/控制速率。

最后,听起来你想成为一个中间件组件。不要试图成为一个应用程序并自行引入线程。如果您是同步的,则只需阻塞/睡眠,如果您被其中之一调用,则使用异步调度框架。

于 2008-12-30T20:05:47.103 回答
1

如果您的库被设计为同步的,那么我建议您忽略限制执行(尽管您可以跟踪费率并至少帮助调用者决定如何遵守限制)。

我现在使用twisted来连接几乎所有的东西。通过拥有一个将请求提交与响应处理分开的模型,它可以很容易地完成这种类型的事情。如果您不希望您的 API 用户必须使用 twisted,那么您至少最好了解他们的 API 以延迟执行。

例如,我有一个 twitter 界面,它代表xmpp users推送相当数量的请求。我没有速率限制,但我确实需要做一些工作来防止所有请求同时发生。

于 2008-12-30T20:43:37.917 回答
1

所以我假设像 import time time.sleep(2) 这样简单的东西不会在请求之间等待 2 秒

于 2008-12-30T23:25:39.120 回答