使用限制队列Guzzle请求

17
我正在开发一个 Laravel 应用程序,使用 Guzzle 6。许多功能依赖于 API,我已经为其创建了一个包装器。
我的包装器是一个单独的类,在 __construct() 中创建 Guzzle 客户端,并具有各种公共函数,这些函数返回 Guzzle 请求的响应。
我使用的 API 每 10 秒钟限制 40 个请求。我正在缓存一些东西,因此很少会达到此限制,但我想知道如果达到限制时,我的应用程序不会死掉!
关于我的应用程序的一些注意事项:
  • API调用仅在过去6小时内没有进行相同的调用时才进行。如果已经调用过,该调用将不会再次进行,响应将直接从我的redis缓存中提供。
  • 在大多数情况下,API调用是通过用户操作进行的。应用程序本身几乎不会接近这些限制。
  • 在大多数情况下,我已经拥有了显示所请求页面所需的数据。可以在后台执行API调用以查看是否需要在我的端上更新任何内容,但如果我已经拥有数据,并且API请求失败,这不会使页面无法使用。
  • 该应用程序已上线,https://likethis.tv,如果您想查看。我正在使用TMDb API。

所以,我的问题是,我应该如何确保不超过API限制? 我的一些想法如下:

请使用 Laravel 队列系统将 Guzzle 请求放入队列中,仅在我们仍有请求时处理它们。如果没有,请等待 10 秒冷却时间过去...请直接为 Guzzle 使用 HandlerStack。不确定是否可能,但我之前已经使用 HandlerStack 缓存响应了。我试图避免引起过多争议的回复,但我相信可能有比上述方法更好和/或更容易的方法,如果它们是好主意,任何指针或建议都将是很棒的。谢谢您的帮助。

1
在这个问题上添加了赏金。我还想知道如何有效地限制我的 API 调用,并且我正在考虑使用队列,但是无法弄清楚正确的方法。 - Jean-Philippe Murray
API的响应应该传递到数据库中还是直接显示给客户端?第二种情况似乎更为困难。 - cre8
API 调用的顺序在某些情况下是否相关? - Kyslik
响应用于更新/创建数据库中的记录。顺序并不重要。目前,我的包装器仅处理404错误,因此我认为我会重新做这一部分,并将错误响应交给刚刚发出调用的任何内容,以便它可以决定该怎么做。例如,如果我在我的DB中有一条记录,而只有API请求出错,我就不需要抛出错误。 - Daniel Dewhurst
4个回答

2

目前提供的信息不足以深入探讨,但是为了帮助您开始,好的API通常在超过其限制时返回429响应代码。

您可以使用Guzzle中的$res->getStatusCode()来检查这一点,并向用户发送消息,如果他们请求太多太快。

您能否提供更多关于您的应用程序正在做什么的信息?您是否在foreach循环中发出请求?视图是否依赖于此API的数据?


API确实返回有用的错误代码,所以这是一种可能性。我可能会按照我在问题上面的评论所说的那样,不在我的包装器中处理错误响应,而是将它们传递给我的控制器,因为在某些情况下,API没有返回我需要的内容并不是世界末日。 - Daniel Dewhurst

2
  1. Wrap your API calls with Jobs and push them to separate queue:

    ApiJob::dispatch()->onQueue('api');
    
  2. Use mxl/laravel-queue-rate-limit package (I'm the author) to rate limit api queue. Add this to config/queue.php:

    'rateLimit' => [
        'api' => [
            'allows' => 40,
            'every' => 10
        ]
    ]
    
  3. Run queue worker:

    $ php artisan queue:work --queue api
    

另请参阅此答案,它是关于IT技术的最初回答。


1
我也在处理同样的问题,我采用了回调架构,我的Client类控制请求流程。目前我正在使用睡眠和检查算法,因为我有3秒的冷却时间。我使用Cache来保存已发出请求的数量。
while(($count = Cache::get($this->getCacheKey(),0)) >= 40){ // Max request
    sleep(1);
}
Cache::set($this->getCacheKey(), ++$count);
// fire request

function getCacheKey(){
    return floor(time()/10); // Cool down time
}

队列似乎是一个更好的选择,我最终会转向它。在将队列放入中间之前,有几件事情需要记住。
  1. 回调式架构,因为您可能需要在队列中保存代码的序列化状态。基于回调的设计将控制权全部交给客户端类。您不必担心代码中的限流问题。
  2. 序列化可能有些棘手,尝试使用__sleep__wakeup
  3. 您可能还想优先处理一些调用,可以从客户端分配配额以进行此类调用。

0

个人认为 Guzzle 不应该处理这种情况,但如果你想让 Guzzle 处理它,我会编写一个中间件来检查响应,如果返回速率限制错误(例如状态码 429),则可以发出自定义错误或等待速率限制结束后再尝试。然而,这可能会导致长时间的响应时间(因为您要等待速率限制)。

我认为 Laravel 队列也不会更好,因为这将使响应异步可用,并且您必须轮询数据库或缓存,无论您在哪里存储结果。(当然,如果您不需要立即使用结果,则可以使用它)

如果此第三方服务直接连接到用户界面,则我可能会在应用程序代码中应用相同的速率限制,并向用户返回错误消息,而不是等待和自动解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接