如何降低此节点循环http请求的速度



我试图用Lambda函数用请求模块调用此API约200-300次。我需要在每个呼叫之间添加秒,这样我就不会得到429响应。我已经尝试了几种不同的方法来实现这一点,但它似乎忽略了代码来减慢速度。

人们通常如何在AWS lambda中减慢这些请求?如果我能在循环中插入一些类似utilities.sleep(1000)的东西,让它在继续之前等待一秒钟,那就太好了。我相信这个问题有一个简单的解决方案,但我所看到的所有例子似乎都使它变得复杂。

function findProjects(items){
    var toggleData = [];
    for( var i = 0; i < items.length; i++ ){
        setTimeout( callToggle( items[i] ), 1000 );
    }
    function callToggle( data ){
        request({
            'url': 'https://www.toggl.com/api/v8/projects/' + data.toggle.data.id,
            'method': 'GET',
            'headers': {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            'auth': {
                'user': 'xxxxxxx',
                'pass': 'api_token'
        }}, function( error, response, body ){
            if( error ) {
                console.log( error );
                context.done( null, error );
            } else {
                console.log(response.statusCode, "toggle projects were listed");
                var info = JSON.parse(body);
                toggleData.push(info);
            }
        });
    }
    findDocument( toggleData );   
}

你可以这样做:

for(var i = 0; i<items.length; i++){
    setTimeout(callToggl, 1000 + (( i * X ) % Y), items[i]);
}

其中Y是最大延迟(1000 + Y)然后你想(5秒)和X是每个呼叫的定时(如X=10: 1000,1010,1020,1030,…)

如果你想每次呼叫15秒:

for(var i = 0; i<items.length; i++){
    setTimeout(callToggl(items[i]), 1000 + ( i * 1000 ));
}

编辑

for(var i = 0; i<items.length; i++){
    setTimeout(callToggl, 1000 + ( i * 1000 ), items[i]);
}

您可以将请求链接在一起:

function findProjects(items){
    var toggleData = [];
    // chain requests with delay
    items.reduce(function (requestChain, item) {
      return requestChain
        .then(callToggle.bind(null, item))
        .then(wait.bind(null, 1000));
    }, Promise.resolve());
    function wait (ms) {
      return new Promise(function (resolve, reject) {
        setTimeout(resolve, ms);
      });
    }
    function callToggle(data) {
        request({
            'url': 'https://www.toggl.com/api/v8/projects/' + data.toggle.data.id,
            'method': 'GET',
            'headers': {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            'auth': {
                'user': 'xxxxxxx',
                'pass': 'api_token'
        }}, function( error, response, body ){
            if( error ) {
                console.log( error );
                context.done( null, error );
            } else {
                console.log(response.statusCode, "toggle projects were listed");
                var info = JSON.parse(body);
                toggleData.push(info);
            }
        });
    }
    findDocument( toggleData );   
}

虽然Node.js是单线程的,但setTimeout不会创建单个同步调用堆栈。当您使用for循环时,您立即将每个后续调用设置为提前1000ms,因此它们都大致在同一时间激活。相反,你可能希望使用第三方承诺库来等待。

你可以这样做:

const Bluebird = require('bluebird');
const $http = require('http-as-promised');
const _ = require('lodash');
const timeout = 1000;
const items = []; 
const headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' };
const auth = { 'user': 'xxxxxxx', 'pass': 'api_token' };
const makeRequest = (id) => {
  const url = 'https://www.toggl.com/api/v8/projects/' + id;
  return $http.get({ url, headers, auth }); 
};
const waitCall = (data) => {
    return Bluebird
    .resolve(makeRequest(data.toggl.data.id))
    .wait(timeout);
};
Bluebird.mapSeries(items, waitCall);
http://bluebirdjs.com/docs/api/promise.mapseries.html

你可能知道,javascript代码不阻塞io(除非使用特定的同步api阻塞你的整个代码,这是一个不好的做法,应该避免,除非你有一个很好的理由阻塞整个代码(加载配置文件在启动…等))

所以你需要做的,就是等待响应

过去统筹这一切有点复杂,但目前,使用——harmony标志(在node中激活最新的js功能),你可以使用闪亮的新异步函数语法。(等待/异步)

内部运行的函数必须声明为异步,然后在每次迭代之后,您需要使用"await"关键字等待该HTTP调用的响应。这个关键字使代码看起来好像在阻塞并等待解析的答案,尽管它不是。

我使用"fetch"而不是"request",因为它与异步函数(基于承诺)配合得很好,但你可以使用任何其他方法,只要你返回一个承诺。(你甚至可以用Promise对象来承诺你现有的基于回调的api,但这会让一切看起来更难看,所以请不要:))

是修改后的代码。我不确定它是否能正常工作。但我认为这个想法很清楚。

在任何情况下,这是一个很好的机会,让你学习如何使用异步函数,如果你还没有使用过它们,它们真的让生活变得更容易。

//making enture function async, so you can use 'await' inside it
async function findProjects(items){
    var toggleData = [];
    for( var i = 0; i < items.length; i++ ){
        //setTimeout( callToggle( items[i] ), 1000 );
        //instead of using a timeout, you need to wait for response before continuing to next iteration
        await response = callToggle(items[i]);
        toggleData.push(JSON.parse(response.body));
    }
    async function callToggle( data ){
        /*request({
            'url': 'https://www.toggl.com/api/v8/projects/' + data.toggle.data.id,
            'method': 'GET',
            'headers': {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            'auth': {
                'user': 'xxxxxxx',
                'pass': 'api_token'
        }}, function( error, response, body ){
            if( error ) {
                console.log( error );
                context.done( null, error );
            } else {
                console.log(response.statusCode, "toggle projects were listed");
                var info = JSON.parse(body);
                toggleData.push(info);
            }
        });*/
        // to make things simpler, use fetch instead of request which is promise based
        var myInit = {  'method': 'GET',
                        'headers': {
                            'Content-Type': 'application/json',
                            'Accept': 'application/json'
                        },
                        'auth': {
                            'user': 'xxxxxxx',
                            'pass': 'api_token'
                        }
                    };
        return fetch("https://www.toggl.com/api/v8/projects/",myInit);
    }
    findDocument( toggleData );   
}

最新更新