9

我有一个带有负载平衡器的 Web 服务,可以将请求映射到多台机器。这些请求中的每一个最终都会向外部 API 发送一个 http 调用,因此我想限制我发送到外部 API 的请求数量。

我目前的设计:

  • 服务在内存中有一个队列,用于存储所有收到的请求
  • 我限制了我们从队列中获取请求并处理它的频率。

当我使用多台机器时,这不起作用,因为每台机器都有自己的队列和速率限制器。例如:当我将我的速率限制器设置为 10,000 个请求/天,并且我使用 10 台机器时,我最终将在满负荷时处理 100,000 个请求/天,因为每台机器每天处理 10,000 个请求。我想限制速率,以便每天只处理 10,000 个请求,同时仍然对这 10,000 个请求进行负载平衡。

我正在使用 Java 和 MYSQL。

4

4 回答 4

2
  1. 使用 memcached 或 redis 为每个客户端保留 api 请求计数器。检查每个请求是否超出速率限制。
  2. 如果你觉得对每个请求进行检查过于昂贵,你可以尝试使用storm来处理请求日志,并异步计算请求计数器。
于 2014-03-28T03:04:48.167 回答
0

为什么不在你的数据库中实现一个简单的计数器并让 API 客户端实现限制呢?

用户代理 -> LB -> 你的服务 -> Q -> 你的 Q 消费者 -> API 客户端-> 外部 API

API客户端检查数字(今天),你可以实现你喜欢的任何速率限制算法。例如,如果数字> 10k,客户端可能会简单地崩溃,让异常将消息放回队列并继续处理直到今天现在明天,所有排队的请求都可以得到处理。

或者,您可以实施分层节流系统,例如,直到 8k,然后每个节点每 5 秒发送 1 条消息,直到达到限制,此时您可以将 503 错误发送回用户代理。

否则,您可以走复杂的路线并实现分布式队列(例如 AMQP 服务器),但这可能无法完全解决问题,因为您唯一的控制机制将是节流,因此您的处理速度永远不会超过每天的最大限制. 例如,您的最大限制是 10k,因此您的速度永远不会超过每 8 秒 1 条消息。

于 2014-03-28T00:26:55.810 回答
0

你说的两件事是:

1)"I would like to rate limit so that only 10,000 requests get processed/day"
2)"while still load balancing those 10,000 requests."

首先,您似乎正在使用分而治之的方法,其中来自最终用户的每个请求都映射到 n 台机器中的一台。因此,为了确保在给定的时间跨度内只处理 10,000 个请求,有两种选择:

1) 实现一个组合器,它将所有 n 台机器的结果路由到
另一个端点,然后外部 API 可以访问该端点。该端点能够记录正在处理的作业数量,如果超过您的阈值,则拒绝该作业。

2) 另一种方法是将您当天处理的作业数量作为变量存储在数据库中。然后,通常的做法是根据作业的初始请求(甚至在您将其传递给您的一台机器之前)检查数据库中的值是否已达到您的阈值。如果已达到阈值,则在开始时拒绝该作业。这与适当的消息相结合,具有为最终用户提供更好体验的优势。

为了确保所有这 10,000 个请求仍然处于负载平衡状态,以便没有一个 CPU 处理的作业比任何其他 cpu 多,您应该使用简单的循环方法将作业分配到 m 个 CPU 上。与 bin/categorization 方法相反,使用循环法,您将确保作业请求尽可能均匀地分布在您的 n 个 CPU 上。循环法的一个缺点是,根据您正在处理的作业类型,当您开始扩大规模时,您可能会复制大量数据。如果这对您来说是一个问题,您应该考虑实现一种形式的局部敏感哈希 (LSH) 函数。虽然一个好的散列函数会尽可能均匀地分布数据,但 LSH 会让你发现一个 CPU 处理的作业比其他 CPU 多。s 如果您选择散列的属性中的偏差很可能发生。与往常一样,两者都有相关的权衡,因此您将最了解您的用例。

于 2014-03-28T01:39:06.133 回答
0

如果您不反对使用库/服务https://github.com/jdwyah/ratelimit-java是获得分布式速率限制的简单方法。

如果性能最受关注,您可以在请求中获取超过 1 个令牌,这样您就不需要向限制器发出 100k 次 API 请求。有关详细信息,请参阅https://www.ratelim.it/documentation/batches

于 2017-02-20T03:24:06.340 回答