由于每秒上限而限制和排队API请求

Tho*_*ggi 53 asynchronous throttling request node.js

我使用mikeal/request来进行API调用.我最频繁使用的API之一(Shopify API).最近推出了一个新的通话限制,我看到的错误如下:

Exceeded 6.0 calls per second for api client. Slow your requests or contact support for higher limits.
Run Code Online (Sandbox Code Playgroud)

我已经进行了升级,但无论我获得多少带宽,我都必须考虑到这一点.对Shopify API的大多数请求都在async.map()函数中,这些函数循环异步请求并收集正文.

我正在寻找任何帮助,也许是一个已经存在的库,它将包裹请求模块并实际阻塞,休眠,限制,分配,管理,同步发出的许多同时请求并限制它们6在一时间 如果不存在这个项目,我没有问题.我只是不知道如何处理这种情况,我希望有某种标准.

我用mikeal/request做了一张票.

Dmi*_*nyi 33

对于替代解决方案,我使用节点速率限制器来包装请求函数,如下所示:

var request = require('request');
var RateLimiter = require('limiter').RateLimiter;

var limiter = new RateLimiter(1, 100); // at most 1 request every 100 ms
var throttledRequest = function() {
    var requestArgs = arguments;
    limiter.removeTokens(1, function() {
        request.apply(this, requestArgs);
    });
};
Run Code Online (Sandbox Code Playgroud)

  • 这里是节点速率限制器的作者.这个库可能更适合所述问题,因为async.queue()只对并发性设置了限制,并且没有时间概念.API速率限制通常是基于时间的(即每秒最多6次调用),可以表示为"var limiter = new RateLimiter(6,'second');`它是像oibackoff这样的解决方案的补充,它将改变行为速度限制已被打中. (16认同)
  • 这只是限制呼叫还是也可以用作排队机制。这意味着如果我超出限制,它会将请求排队并在刷新限制后再次开始调用? (2认同)

Cam*_*hez 22

npm简单的速率限制似乎是一个很好的解决了这个问题.

而且,它比node-rate-limiter和更容易使用async.queue.

这是一个片段,显示如何将所有请求限制为每秒十个.

var limit = require("simple-rate-limiter");
var request = limit(require("request")).to(10).per(1000);
Run Code Online (Sandbox Code Playgroud)


Dan*_*Dan 14

我遇到了各种API的相同问题.AWS也因节流而闻名.

可以使用几种方法.你提到了async.map()函数.你试过async.queue()吗?队列方法应允许您设置固定限制(如6),超过该数量的任何内容都将放入队列中.

另一个有用的工具是oibackoff.如果您从服务器收到错误并再次尝试,该库将允许您退出您的请求.

包装两个库以确保你的基础都被覆盖是很有用的:async.queue以确保你没有超出限制,并且oibackoff确保你在服务器告诉你的时候获得请求的另一个镜头有一个错误.

  • https://caolan.github.io/async/docs.html#queue不会限制(每秒/分钟)。它只是异步操作的数量。 (2认同)

Man*_*ddy 8

在异步模块中,此请求的功能已关闭为"不会修复"

有一个使用leakybucket或令牌桶模型的解决方案,它实现了"限制器"npm模块作为RateLimiter.

RateLimiter,请参见此处的示例:https://github.com/caolan/async/issues/1314#issuecomment-263715550

另一种方法是使用PromiseThrottle,我用过这个,工作示例如下:

var PromiseThrottle = require('promise-throttle');
let RATE_PER_SECOND = 5; // 5 = 5 per second, 0.5 = 1 per every 2 seconds

var pto = new PromiseThrottle({
    requestsPerSecond: RATE_PER_SECOND, // up to 1 request per second
    promiseImplementation: Promise  // the Promise library you are using
});

let timeStart = Date.now();
var myPromiseFunction = function (arg) {
    return new Promise(function (resolve, reject) {
        console.log("myPromiseFunction: " + arg + ", " + (Date.now() - timeStart) / 1000);
        let response = arg;
        return resolve(response);
    });
};

let NUMBER_OF_REQUESTS = 15;
let promiseArray = [];
for (let i = 1; i <= NUMBER_OF_REQUESTS; i++) {
    promiseArray.push(
            pto
            .add(myPromiseFunction.bind(this, i)) // passing am argument using bind()
            );
}

Promise
        .all(promiseArray)
        .then(function (allResponsesArray) { // [1 .. 100]
            console.log("All results: " + allResponsesArray);
        });
Run Code Online (Sandbox Code Playgroud)

输出:

myPromiseFunction: 1, 0.031
myPromiseFunction: 2, 0.201
myPromiseFunction: 3, 0.401
myPromiseFunction: 4, 0.602
myPromiseFunction: 5, 0.803
myPromiseFunction: 6, 1.003
myPromiseFunction: 7, 1.204
myPromiseFunction: 8, 1.404
myPromiseFunction: 9, 1.605
myPromiseFunction: 10, 1.806
myPromiseFunction: 11, 2.007
myPromiseFunction: 12, 2.208
myPromiseFunction: 13, 2.409
myPromiseFunction: 14, 2.61
myPromiseFunction: 15, 2.811
All results: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
Run Code Online (Sandbox Code Playgroud)

我们可以清楚地看到输出的速率,即每秒5次调用.


dja*_*ski 8

我使用现代香草 JS 的解决方案:

function throttleAsync(fn, wait) {
  let lastRun = 0;

  async function throttled(...args) {
    const currentWait = lastRun + wait - Date.now();
    const shouldRun   = currentWait <= 0;

    if (shouldRun) {
      lastRun = Date.now();
      return await fn(...args);
    } else {
      return await new Promise(function(resolve) {
        setTimeout(function() {
          resolve(throttled());
        }, currentWait);
      });
    }
  }

  return throttled;
}
Run Code Online (Sandbox Code Playgroud)

用法:

const throttledRun = throttleAsync(run, 1000);
Run Code Online (Sandbox Code Playgroud)


min*_*vor 6

其他解决方案不符合我的口味。进一步研究,我发现了promise-ratelimit,它为你提供了一个api,你可以简单地await

var rate = 2000 // in milliseconds
var throttle = require('promise-ratelimit')(rate)

async function queryExampleApi () {
  await throttle()
  var response = await get('https://api.example.com/stuff')
  return response.body.things
}
Run Code Online (Sandbox Code Playgroud)

上面的示例将确保您最多api.example.com仅每 2000 毫秒进行一次查询。换句话说,第一个请求不会等待2000ms。