【问题标题】:How to make it so that I can execute say 10 promises at a time in javascript to prevent rate limits on api calls?如何做到这一点,以便我可以在 javascript 中一次执行 10 个承诺,以防止对 api 调用的速率限制?
【发布时间】:2017-11-23 18:33:58
【问题描述】:

我有 1000 条记录需要访问速率受限的 API 端点。我想让它在任何给定时间都只有 5 个 URL 调用,这样我就不会同时发出 1000 个请求。我怎样才能做到这一点?我有以下内容:

var Promise = require("bluebird");
var geocoder = Promise.promisifyAll(require('geocoder'));
var fs = require('fs');
var async = require('async');
var parse = require('csv-parse/lib/sync');
var inputFile = './myaddresses.txt'
var file = fs.readFileSync(inputFile, "utf8");

var records = parse(file, {columns: true});
var promises = [];
for(var i = 0; i < records.length; i++) {
    var placeName = records[i]['Place Name'];
            promises.push(geocoder.geocodeAsync(placeName));    
}

Promises.all(promises).then(function(result) {
  result.forEach(function(geocodeResponse) {
  console.log(geocodeResponse);
  })
}

【问题讨论】:

  • 您必须保持的实际速率限制是多少?它是一些请求数/秒吗?还是别的什么?
  • 我不确定,这是 Google 地理编码 API。
  • 如果你想保持在速率限制之下,你应该做一些谷歌研究,看看速率限制是什么以及它是如何衡量的。如果没有这些信息,您只是在猜测和测试解决方案,它永远不会非常优化并且可能不一致。
  • 在我看来 here 好像是 50 个请求/秒,std 帐户每天 2500 个请求。您可以购买更多访问权限。

标签: javascript promise bluebird


【解决方案1】:

为了限制一次进行中的并发请求的数量,我建议使用 Bluebird 的 Promise.map(),它提供了并发选项。它将为您执行以下所有操作:

  1. 迭代您的数组
  2. 将并发请求数限制为您将并发选项设置为的任何值
  3. 在最终结果数组中按顺序收集所有结果

您将如何使用它:

const Promise = require('bluebird');

Promise.map(records, r => {
    let placeName = r['Place Name'];
    return geocoder.geocodeAsync(placeName));
}, {concurrency: 5}).then(results => {
    // all results here
}).catch(err => {
    // process error here
});

注意:速率限制通常与并发请求数不完全相同。限制并发请求的数量将使您更有可能保持在速率限制之下,但不能保证。有特定的速率限制模块可以更直接地管理速率限制。


您可以使用 Bluebird 的 .delay() 为每个请求添加延迟。

const Promise = require('bluebird');

Promise.map(records, r => {
    let placeName = r['Place Name'];
    return geocoder.geocodeAsync(placeName)).delay(500);
}, {concurrency: 5}).then(results => {
    // all results here
}).catch(err => {
    // process error here
});

处理某些类型的速率限制的经典算法称为leaky bucket algorithm


如果您的限制是 50 个请求/秒,那么您只需确保您的并发数乘以您的延迟值绝不允许超过 50/秒。

【讨论】:

  • 限速似乎还是个问题,有没有办法说,每5个后加一个延迟秒?
  • @Rolando - 我已经向您展示了如何为每个请求添加延迟(请参阅第二个代码块),但正如我的回答所说,并发和延迟并不是您通常衡量速率限制的严格方式.足够低的并发值和足够长的延迟可能会使您低于速率限制值,但更全面的解决方案是实际管理请求数量以完全避免正在测量的速率限制。如果您可以准确地分享速率限制,那么我们可以更具体地为您提供帮助。有些模块可以管理特定的速率限制。
  • @Rolando - 仅供参考,经典的速率限制算法称为leaky bucket algorithm
  • @Rolando - 我添加了一些关于如何将事情保持在 50 个请求/秒以下的信息。
【解决方案2】:

在没有库的情况下使用瀑布模式,并使用竞态条件在每次迭代中通过 reduce 解决。并且可以通过在 Array.from 中指定数组的长度来限制调用次数。

var promise = Array.from({ length: 5 }).reduce(function (acc) {
  return acc.then(function (res) {
    return run().then(function (result) {
      res.push(result);
      return res;
    });
  });
}, Promise.resolve([]));


var guid = 0;
function run() {
  guid++;
  var id = guid;
  return new Promise(resolve => {
    // resolve in a random amount of time
    setTimeout(function () {
      console.log(id);
      resolve(id);
    }, (Math.random() * 1.5 | 0) * 1000);
  });
}

【讨论】:

    猜你喜欢
    • 2012-04-02
    • 1970-01-01
    • 1970-01-01
    • 2019-07-03
    • 2017-05-07
    • 1970-01-01
    • 1970-01-01
    • 2016-06-24
    • 1970-01-01
    相关资源
    最近更新 更多