2017-06-21 39 views
1

我有1000条记录需要击中速率受限的API端点。我想让它在任何时候只有5个URL的调用,这样我就不会同时发出1000个请求。我怎样才能做到这一点?我有以下几点:如何使它能够在javascript中一次执行10个承诺以防止API调用的速率限制?

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); 
    }) 
} 
+0

什么是你必须留在实际速率限制?这是几个请求/秒?或者是其他东西? – jfriend00

+0

我不确定,这是Google Geocoding API。 – Rolando

+0

如果你想要保持在限制的范围内,你应该做一些谷歌研究,看看费率限制是什么以及如何测量。没有这些信息,你只是在做一个猜测和测试解决方案,它永远不会被优化,并且可能会不一致。 – jfriend00

回答

0

来限制飞行一次的并发请求数,我推荐使用蓝鸟的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 /秒。

+0

速率限制似乎仍然是一个问题,有没有办法说,每增加一个延迟几秒后? – Rolando

+0

@Rolando - 我已经向您展示了如何为每个请求添加延迟(请参阅第二个代码块),但正如我的回答所言,并发性和延迟并不严格地如何评估您的速率限制。足够低的并发值和足够长的延迟可能会使您低于速率限制值,但更全面的解决方案是实际管理请求的数量以准确避免测量速率限制。如果您可以准确分享费率限额,那么我们可以更具体地为您提供帮助。有些模块可以管理到特定的速率限制。 – jfriend00

+0

@Rolando - FYI,经典的限速算法被称为[漏桶算法](https://en.wikipedia.org/wiki/Leaky_bucket)。 – jfriend00

0

使用没有库的瀑布模式,并使用竞争条件在每次迭代中使用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); 
 
    }); 
 
}