2

我有一个“密钥”,当密钥在 1 分钟内达到其配额(1 分钟存储桶不是滑动窗口)端点时,我可以每分钟调用 500 次休息端点(每个密钥可以是 200、300 或 500)返回带有等待延迟的 429(速率受限)。一旦延迟过去,我可以再次使用这个键。

我的 API 是一种代理(客户端调用我的 API,我的 API 通过提供其中一个密钥来调用其他服务)我的 API 将堆叠我所有的用户密钥,这样在某种意义上它就变成了一个大密钥。并且当我的客户中的“任何”调用我的 api 时,我的系统会巧妙地选择 1 个仍然具有配额的键,并且当该键达到配额时,它会将其从可用键列表、队列或任何结构中删除

我很难为它选择一个好的数据结构和逻辑。我想到的是这样的

1) 获得一个仍然有配额并且至少有 1 个剩余通话可用的密钥 -1a) 如果没有可用的密钥,只需“等待”直到有一个可用

2)调用端点

3) 减少 1 的密钥配额,如果密钥受到速率限制,则将其从“队列”或“列表”或其他任何内容中删除。

4)回到1

请注意,我的 API 调用的端点会返回有用的信息,例如“requestRemaining”,这些信息指示密钥在受到速率限制之前还剩下多少请求。“速率延迟”指示何时可以再次使用密钥,以防它受到速率限制。

我认为一个好的解决方案是主动了解密钥何时会受到速率限制,而不是仅依赖端点响应。像这样,我们避免使用已达到配额的密钥“免费”调用端点。

我已经尝试过使用 DelayQueue 和一些信号量来阻止没有可用的密钥,但是我有一个问题,提前知道密钥是否会受到速率限制。我想在“请求”级别而不是关键级别并发。这意味着我不希望客户端在发出请求之前锁定密钥。如果一个密钥的配额为 500,我希望最多 500 个并发客户端使用 1 个单一密钥;

我很乐意听取更有经验的人的意见,了解他们将如何解决这个问题。

4

1 回答 1

0

如果你不介意阻塞一个键,你可以有一个键的延迟队列, getDelay() 将是键速率限制的时间(如果键仍在配额中,则为 0)

private void executeRequest(...) {
 long delay = 0L
 try {
  // .take from the queue (it will remove it from the queue)
  // execute your request
  // if you get 429 take the delay from the response header
 } finally {
  //put back the key in the queue but setting the "delay" (0 or whatever the header had
 }

}

在使用之前移除密钥的事实将避免其他任何人使用该密钥。在最后关闭时,您总是放回密钥,并且由于 DelayQeeue,当达到延迟时,该密钥只​​能由 .take() 使用

编辑:抱歉刚刚看到你编辑了你的问题,让它出现在请求而不是键上。在那种情况下......我不确定,但我很好奇其他人会如何处理这个问题

于 2017-08-05T13:08:51.127 回答