有限制地排队 Guzzle 请求
Queuing Guzzle Requests With Limits
我正在使用 Guzzle 6 开发 Laravel 应用程序。很多功能都依赖于 API,我已经为其创建了一个包装器。
我的包装器是一个 class,它在 __construct()
中创建 Guzzle 客户端,并具有各种 public 函数,这些函数 return 响应 Guzzle 请求。
我正在使用的 API 每 10 秒有 40 个请求的限制。我正在缓存内容,因此很少会达到此限制,但我想知道我的应用程序不会在达到此限制时死掉!
关于我的应用程序的一些说明:
- API 只有在过去 6 小时内没有进行过相同的呼叫时才会进行呼叫。如果有,则永远不会进行调用,直接从我的 redis 缓存提供响应。
- 在大多数情况下,API 调用是通过用户操作进行的。应用程序本身永远不会接近达到这些限制。
- 在大多数情况下,我已经拥有向用户显示请求页面所需的数据。 API 调用可能会在后台完成,以查看我这边是否需要更新任何内容,但如果我已经拥有数据,并且 API 请求失败,则不会呈现页面没用。
- 该应用已上线,https://likethis.tv if you'd like to look. I'm using TMDb API。
所以,我的问题是,我应该如何确保我没有达到这个限制?我的一些想法如下:
- 使用 Laravel 排队系统将 Guzzle 请求放入队列中,并且仅在我们仍有请求时才处理它们。如果没有,请等到 10 秒冷却时间过去...
- 直接为 Guzzle 使用
HandlerStack
。不确定这是否可行,但我之前使用 HandlerStack
来缓存响应。
我尽量不引起过于自以为是的回应,但我相信可能有比上述更好的 and/or 更简单的方法,或者如果它们是好主意,任何指示或建议都可以太好了。
提前致谢。
没有足够的信息来真正深入研究这个问题,但为了让您开始,好的 API 通常 return 当您超过限制时会收到 429 响应代码。
您可以使用 guzzle 中的 $res->getStatusCode()
来检查这一点,并在用户发出太多请求的速度过快时向用户闪回一条消息。
你能提供更多关于你的应用程序的信息吗?您是在 foreach 循环中发出请求吗?视图是否依赖于来自此 API 的数据?
我个人认为 Guzzle 不应该处理这种情况,但如果您希望 Guzzle 处理它,我会编写一个中间件来检查响应,如果它 return 是速率限制错误(例如状态代码 429) .然后您可以发出自定义错误或等到速率限制结束后再试。然而,这可能会导致较长的响应时间(因为您等待速率限制)。
我不认为 Laravel 队列会更好,因为这会使响应异步可用,并且无论将结果存储在何处,您都必须轮询数据库或缓存。 (当然,如果您不需要立即获得结果,它也可以工作)
如果此第三方服务直接连接到面向用户的界面,我可能会应用相同的速率限制(在您的应用程序代码中)和 return 向用户发送错误消息,而不是等待和自动解析问题。
我也在处理同样的问题,我更喜欢基于回调的架构,其中我的 Client
class 控制请求流。目前我正在做睡眠和检查算法。我为我工作,因为我有 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
}
排队似乎是更好的选择,我最终会转向排队。在将队列置于中间之前,您需要牢记一些事项。
- 基于回调的架构,因为您可能需要在队列中保存代码的序列化状态。基于回调的设计将把所有控制权交给
Client
Class。您不必担心代码中的限制。
- 序列化可能很棘手,请尝试
__sleep
和 __wakeup
。
- 您可能还想优先处理少数呼叫,您可以为此类呼叫从客户端分配配额。
用 Jobs 包装你的 API 调用并将它们推送到单独的队列:
ApiJob::dispatch()->onQueue('api');
使用 mxl/laravel-queue-rate-limit 包(我是作者)来限制 api
队列的速率。将此添加到 config/queue.php
:
'rateLimit' => [
'api' => [
'allows' => 40,
'every' => 10
]
]
运行队列工作者:
$ php artisan queue:work --queue api
另见 this answer。
我正在使用 Guzzle 6 开发 Laravel 应用程序。很多功能都依赖于 API,我已经为其创建了一个包装器。
我的包装器是一个 class,它在 __construct()
中创建 Guzzle 客户端,并具有各种 public 函数,这些函数 return 响应 Guzzle 请求。
我正在使用的 API 每 10 秒有 40 个请求的限制。我正在缓存内容,因此很少会达到此限制,但我想知道我的应用程序不会在达到此限制时死掉!
关于我的应用程序的一些说明:
- API 只有在过去 6 小时内没有进行过相同的呼叫时才会进行呼叫。如果有,则永远不会进行调用,直接从我的 redis 缓存提供响应。
- 在大多数情况下,API 调用是通过用户操作进行的。应用程序本身永远不会接近达到这些限制。
- 在大多数情况下,我已经拥有向用户显示请求页面所需的数据。 API 调用可能会在后台完成,以查看我这边是否需要更新任何内容,但如果我已经拥有数据,并且 API 请求失败,则不会呈现页面没用。
- 该应用已上线,https://likethis.tv if you'd like to look. I'm using TMDb API。
所以,我的问题是,我应该如何确保我没有达到这个限制?我的一些想法如下:
- 使用 Laravel 排队系统将 Guzzle 请求放入队列中,并且仅在我们仍有请求时才处理它们。如果没有,请等到 10 秒冷却时间过去...
- 直接为 Guzzle 使用
HandlerStack
。不确定这是否可行,但我之前使用HandlerStack
来缓存响应。
我尽量不引起过于自以为是的回应,但我相信可能有比上述更好的 and/or 更简单的方法,或者如果它们是好主意,任何指示或建议都可以太好了。
提前致谢。
没有足够的信息来真正深入研究这个问题,但为了让您开始,好的 API 通常 return 当您超过限制时会收到 429 响应代码。
您可以使用 guzzle 中的 $res->getStatusCode()
来检查这一点,并在用户发出太多请求的速度过快时向用户闪回一条消息。
你能提供更多关于你的应用程序的信息吗?您是在 foreach 循环中发出请求吗?视图是否依赖于来自此 API 的数据?
我个人认为 Guzzle 不应该处理这种情况,但如果您希望 Guzzle 处理它,我会编写一个中间件来检查响应,如果它 return 是速率限制错误(例如状态代码 429) .然后您可以发出自定义错误或等到速率限制结束后再试。然而,这可能会导致较长的响应时间(因为您等待速率限制)。
我不认为 Laravel 队列会更好,因为这会使响应异步可用,并且无论将结果存储在何处,您都必须轮询数据库或缓存。 (当然,如果您不需要立即获得结果,它也可以工作)
如果此第三方服务直接连接到面向用户的界面,我可能会应用相同的速率限制(在您的应用程序代码中)和 return 向用户发送错误消息,而不是等待和自动解析问题。
我也在处理同样的问题,我更喜欢基于回调的架构,其中我的 Client
class 控制请求流。目前我正在做睡眠和检查算法。我为我工作,因为我有 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
}
排队似乎是更好的选择,我最终会转向排队。在将队列置于中间之前,您需要牢记一些事项。
- 基于回调的架构,因为您可能需要在队列中保存代码的序列化状态。基于回调的设计将把所有控制权交给
Client
Class。您不必担心代码中的限制。 - 序列化可能很棘手,请尝试
__sleep
和__wakeup
。 - 您可能还想优先处理少数呼叫,您可以为此类呼叫从客户端分配配额。
用 Jobs 包装你的 API 调用并将它们推送到单独的队列:
ApiJob::dispatch()->onQueue('api');
使用 mxl/laravel-queue-rate-limit 包(我是作者)来限制
api
队列的速率。将此添加到config/queue.php
:'rateLimit' => [ 'api' => [ 'allows' => 40, 'every' => 10 ] ]
运行队列工作者:
$ php artisan queue:work --queue api
另见 this answer。