【问题标题】:Handling (queuing) requests to a web service which calls rate limited external API处理(排队)对调用速率受限外部 API 的 Web 服务的请求
【发布时间】:2016-02-08 15:29:53
【问题描述】:

我有一个使用 Flask 框架公开的 Web 服务。

该服务作为外部API使用,每秒调用次数有限制。

在正常情况下,对我的 API 的多次调用会导致生成多个线程并调用外部 API,而对每秒的请求数没有任何控制。

有没有一种方法可以让我对 Web 服务的请求进行排队,然后以节流的方式调用外部 API。

也欢迎任何其他想法。


编辑:

  1. 我已经知道外部 API 的速率(每秒 1 个请求)

  2. 如果请求我的 API 的客户端在获得结果之前必须等待一段时间(几秒/分钟,具体取决于我的负载),我没问题。

  3. 我不希望我的 API 客户端得到失败的结果。即我不希望他们一次又一次地打电话。如果我已经以可能的​​最大速率访问了外部 API,那么当速率下降时,对我的 API 的请求应该排队并处理。

  4. 我读到了CeleryRedis。我能否将 Web 服务调用排队到这些队列中并稍后处理它们?

【问题讨论】:

  • 超过请求时您希望发生什么?您希望阻止对您的 API 的请求吗?或者你会回复你自己的http 429 回复吗?
  • 我希望对我的 API 的请求在需要时等待。 (排队可能是!?)并在我可以满足外部 API 的每秒请求标准时返回结果。如果调用我的请求需要几秒钟才能返回,我没有问题。
  • Celery 通常用于从 Web 请求中启动长时间运行的进程。通常,除了知道请求进入 celery 队列之外,您的客户不会得到有用的回复,除非您实际上正在做一些事情以在 celery 任务完成时提醒您的客户,例如向他们发送电子邮件或发送他们使用 socket.io 或其他东西的消息。

标签: python web-services flask webserver rate-limiting


【解决方案1】:

一种方法是包装请求,这样速率限制失败将导致指数退避,直到找到可接受的速率。

在下面的示例中,它将不断重试请求,直到成功,每次失败时在请求之间等待的时间越来越长,直至允许重试的最大次数 (n_max)。它等待重​​试请求的秒数呈指数增长(1、2、4、8、16、32 等)。

这是一个使用requests 的示例。捕获错误和识别速率限制错误的细节将取决于您用于发出请求的库以及外部 api 返回的错误类型,但退避算法应该相同。

def call_backoff(request_func, n_max=10):
    n = 0
    while n < n_max:
        error = False
        try:
            response = request_func()
        except errors.HttpError as e:
            # You can test here if this is a rate error
            # and not some other error.  You can decide how to handle
            # other errors.
            error = True
            if not_a_rate_error:
                raise

         if response.status_code == 429:
             error = True

         if not error:
             return response

         milli = random.randint(1, 1000)
         secs = (2 ** n) + milli / 1000.0
         time.sleep(secs)
         n += 1

    # You can raise an error here if you'd like
    return None

【讨论】:

  • 我已经知道外部 API 的相同速率限制(每秒最多 1 个请求)。但是,如果超出外部 API 限制,我不想返回“空”结果。我只是想让它们排队处理得慢一点,然后返回有效结果。
  • 使用你的方法,客户必须一次又一次地调用我的 API 才能得到结果,对吧?
  • 通过指数退避,它将不断重试对外部 api 的请求,而对您的 api 的请求会阻塞,因此您的客户不必重试他们的请求。每次失败,它都会等待越来越长的时间来重试下一个请求。在我上面给出的示例中(n_max 实际上很高),它将重试多达 10 次。在第 9 次尝试后,它将等待超过 15 分钟,然后再发出第 10 次请求。不过,这可能太高了。最好用错误来响应您的客户。
  • @Brended 这也可能导致饥饿?当一个线程在等待外部 API 时,其他人可能会在两者之间访问它,从而再次导致等待线程退出?
  • @AmitTomar 这是一种可能性,但通常不能保证请求按照它们发送的顺序返回。如果您希望请求以更“公平”的方式阻塞,您可以使用共享状态变量来指示您的 api 当前是否“阻塞”以阻塞新请求,直到旧请求完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-07
  • 2022-11-13
  • 2019-02-28
  • 2019-05-13
  • 1970-01-01
  • 2012-10-01
  • 2022-01-09
相关资源
最近更新 更多