延迟函数执行(API 调用)以在时间段内执行 n 次



我正在尝试编写处理对特定 API 的请求的后端功能,但此 API 有一些限制性配额,尤其是对于每秒请求数。我想创建 API 抽象层,如果请求/秒太多,它能够延迟函数执行,所以它的工作原理是这样的:

  1. 新请求到达(简单来说 - 调用库方法)
  2. 根据给定的限制(请求/秒)检查此请求是否可以立即执行
  3. 如果无法执行,请将其执行延迟到下一个可用时刻
  4. 如果此时有新请求到达,请进一步延迟其执行或将其放在某个执行队列中

我在等待队列长度方面没有任何限制。请求是函数调用,其中 node.js 回调作为响应数据的最后一个参数。

我想为每个请求添加延迟,这将等于请求之间的最小可能插槽(表示为最小毫秒/请求),但它可能有点低效(总是在发送响应之前延迟函数)。

您知道可以为我提供此类功能的库或简单解决方案吗?

保存最后一个请求的时间戳。

每当您有新的传入请求时,请检查此后是否经过了最小间隔,如果没有,请将函数放入队列中,然后计划作业(除非已经计划了一个):

setTimeout(
  processItemFromQueue,
  (lastTime + minInterval - new Date()).getTime()
)

processItemFromQueue从队列的前面(shift)获取一个作业,然后重新调度自己,除非队列为空。

这个问题的明确答案(也是最好的答案)来自 API 文档本身。我们用了几个月,它完美地解决了我的问题。

在这种情况下,与其编写一些复杂的队列代码,最好的方法是利用JS处理异步代码的可能性,然后自己编写简单的退避,或者使用许多伟大的库之一来使用它。

因此,如果您偶然发现任何 API 限制(例如配额、5xx 等),您应该使用退避再次递归运行查询,但延迟会增加(有关退避的更多信息可以在这里找到:https://en.wikipedia.org/wiki/Exponential_backoff)。而且,如果最后,在给定的次数后再次失败 - 优雅地返回有关 API 不可用的错误。

下面的使用示例(取自 https://www.npmjs.com/package/backoff):

var call = backoff.call(get, 'https://someaddress', function(err, res) {
    console.log('Num retries: ' + call.getNumRetries());
    if (err) {
        // Put your error handling code here.
        // Called ONLY IF backoff fails to help
        console.log('Error: ' + err.message); 
    } else {
        // Put your success code here
        console.log('Status: ' + res.statusCode);
    }
});
/*
 * When to retry. Here - 503 error code returned from the API
 */
call.retryIf(function(err) { return err.status == 503; }); 
/*
 * This lib offers two strategies - Exponential and Fibonacci. 
 * I'd suggest using the first one in most of the cases
 */
call.setStrategy(new backoff.ExponentialStrategy());
/*
 * Info how many times backoff should try to post request
 * before failing permanently
 */
call.failAfter(10);
// Triggers backoff to execute given function
call.start();

NodeJS有许多退避库,利用Promise风格,回调风格甚至事件风格的退避处理(上面的例子是提到的第二个)。如果您了解退避算法本身,它们真的很容易使用。由于退避参数可以存储在配置中,如果退避经常失败,可以调整它们以获得更好的结果。

最新更新