我有一个“键”,让我每分钟拨打一次休息终点500次(每个键可以是200,300或500) 当密钥在1分钟内达到其配额(1分钟桶不滑动窗口)时,端点返回429(速率受限)并等待延迟。一旦延迟过去,我可以再次使用此密钥。
我的API是一种代理(客户端调用我的API,我的API通过提供其中一个键来调用其他服务) 我的API会堆叠我的所有用户密钥,因此在某种意义上它会变成一个大密钥。 当我的客户的'任何'调用我的api时,我的系统会巧妙地选择1个仍具有配额的密钥,当该密钥达到配额时,它会将其从可用密钥列表,队列或任何结构中删除
我很难为它选择一个好的数据结构和逻辑。我的想法是这样的
1)获得一个仍具有配额且至少有1个剩余呼叫可用的密钥-1a)如果没有可用的密钥只是'等待'直到有一个可用
2)调用端点
3)减少1的密钥配额,如果密钥获得速率限制,则将其从“队列”或“列表”或其他任何内容中删除。
4)回到1
请注意,我的API调用的端点会返回有用的信息,例如“requestRemaining”,指示密钥在速率受限之前仍有多少请求。 “速率延迟”,表示当密钥被限制时,何时可以再次使用密钥。
我认为这里一个好的解决方案是主动知道密钥何时会受到限制而不是仅仅依赖于端点响应。像这样,我们避免使用已达到配额的密钥对端点进行“无用”调用。
我已经尝试过使用DelayQueue和一些信号量来阻止没有密钥可用但我提前知道密钥是否会受到速率限制。 我希望在“请求”级别并发,而不是在关键级别。这意味着我不希望客户端在发出请求之前锁定密钥。如果密钥的配额为500,我希望最多500个并发客户端使用1个单密钥;
我希望得到更有经验的人如何接近这一点。
答案 0 :(得分:0)
如果您不介意阻止某个密钥,您可以使用密钥的DelayQueue,则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()获得钥匙
编辑:抱歉,只是看到你编辑了你的问题,让它出现在请求而不是密钥上。在那种情况下...我不确定,但我很好奇其他人如何处理这个问题