按限制排队Guzzle请求

时间:2017-05-21 01:06:02

标签: php laravel laravel-5 guzzle guzzle6

我正在使用Guzzle 6处理Laravel应用程序。许多功能依赖于API,我已经为其创建了一个包装器。

我的包装器是一个单独的类,它在__construct()中创建了Guzzle客户端,并且有各种公共函数可以返回Guzzle请求的响应。

我使用的API每10秒限制40个请求。我正在缓存这些东西,所以达到这个限制是非常罕见的,但是我想知道我的应用程序如果真的死了就不会死!

关于我的应用的一些注意事项:

  • 只有在过去6小时内未进行同一次通话时才会进行API通话。如果有,则永远不会进行调用,并且直接从我的redis缓存中提供响应。
  • 在大多数情况下,API调用是通过用户操作进行的。该应用程序本身永远不会接近这些限制。
  • 在大多数情况下,我已经拥有了向用户显示所请求页面所需的数据。可以在后台进行API调用以查看是否需要更新任何内容,但如果我已经拥有数据,并且API请求失败,则不会使页面无效。
  • 该应用是实时的,https://likethis.tv如果你想看一下。我使用TMDb API。

所以,我的问题是,我应该如何确保我没有达到这个限制?我的一些想法如下:

  • 使用Laravel排队系统将Guzzle请求放入队列,只有在我们仍有请求时才处理它们。如果没有,请等到10秒的冷却时间过去......
  • 直接使用HandlerStack表示Guzzle。不确定这是否可行,但我之前已使用HandlerStack缓存响应。

我试图不挑起过于自以为是的反应,但我确信这可能比上面的更好和/或更简单,或者如果它们是好主意,任何指针或建议会很棒。

提前致谢。

4 个答案:

答案 0 :(得分:2)

没有足够的信息可以深入挖掘这一点,但为了让您入门,优秀的API通常会在您超出其限制限制时返回429响应代码。

你可以使用guzzle中的$res->getStatusCode()检查这一点,如果他们提出的请求太快,就会向用户发送消息。

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

答案 1 :(得分:1)

  1. 使用Jobs来包装API调用,并将其推送到单独的队列:

    ApiJob::dispatch()->onQueue('api');
    
  2. 使用mxl/laravel-queue-rate-limit程序包(我是作者)来限制api队列的速率。将此添加到config/queue.php

    'rateLimit' => [
        'api' => [
            'allows' => 40,
            'every' => 10
        ]
    ]
    
  3. 运行队列工作器:

    $ php artisan queue:work --queue api
    

另请参阅this answer

答案 2 :(得分:0)

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

我不认为Laravel队列会更好,因为这会使响应异步可用,并且无论您在何处存储结果,都必须轮询数据库或缓存。 (当然,如果您不希望结果立即可用,它可以工作)

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

答案 3 :(得分:0)

我也在研究相同的问题,我更喜欢基于回调的体系结构,其中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. 基于回调的体系结构,因为您可能需要在队列中保存序列化的代码状态。基于回调的设计将控制对Client类的所有控制。您不必担心代码中的限制。
  2. 序列化可能很棘手,请尝试__sleep__wakeup
  3. 您可能还希望优先处理少数呼叫,您可以从客户端为此类呼叫分配配额。