Node.js:如何暂停/恢复for循环

时间:2015-12-04 11:22:27

标签: node.js maxmind

我正在运行for循环来生成所有可能的IPv4地址,然后在maxmind的mmdb文件中查找它们。问题是,虽然for循环太快,但maxmind查找相对非常慢,因此进程速度变慢,最终我的系统在经过400k次迭代后冻结。如果我正在读取某些流,我会在读取每个10k ip地址后暂停流,并且只有在从mmdb文件中查找所有这些地址时才会恢复。但是我怎样才能对for循环进行这样的控制呢?

connectToMaxMindDatabases().then(function (done){
    for(var i=0; i<256; i++){
        for(var j=0; j<256; j++){
            for(var k=0; k<256; k++){
                for(var l=0; l<256; l++){
                    count++;
                    if(count % 1000 == 0){
                        console.log("count", count);
                    }
                    var newIP = getIPV4([i,j,k,l])
                    ispDB.getGeoDataAsync(newIP).then(function (result){
                        if(result){
                            console.log(count, newIP, result);
                            // process.exit();
                        }
                    });
                }
            }
        }
    }       
})


function getIPV4(bytes){
    return bytes.join(".")
}

2 个答案:

答案 0 :(得分:0)

我不确定,但是process.nextTick可能有所帮助 https://nodejs.org/api/process.html#process_process_nexttick_callback_arg

我真的不确定,但可能会使用以下代码:

connectToMaxMindDatabases().then(function (done){
    for(var i=0; i<256; i++){
        process.nextTick(function(){
            for(var j=0; j<256; j++){
                process.nextTick(function(){
                    for(var k=0; k<256; k++){
                        process.nextTick(function(){
                            for(var l=0; l<256; l++){
                                process.nextTick(function(){
                                    count++;
                                    if(count % 1000 == 0){
                                        console.log("count", count);
                                    }
                                    var newIP = getIPV4([i,j,k,l])
                                    ispDB.getGeoDataAsync(newIP).then(function (result){
                                        if(result){
                                            console.log(count, newIP, result);
                                            // process.exit();
                                        }
                                    });
                                }
                            }
                        })
                    }
                }
            }
        }
    }       
})

根据我的知识,这里发生了什么: 所有for循环都是同步运行的,async ispDB.getGeoDataAsync(newIP)调用正在排队,但是它的回调(.then函数)无法执行,因为4 for循环阻止了事件循环。

答案 1 :(得分:0)

问题的根源在于底层OS / Node服务器的资源管理。具有这样的迭代次数的for循环将冻结在该特定硬件上,而它可能仅在更强的计算机上稍后冻结或者根本不冻结。解决方案是首先收集数组中的所有IP地址,然后以一次只处理一个段的方式处理它们,等待完成并移动到下一个段。

使用此类细分和Bluebird的Promse.each可以实现一种可能的解决方案。

  

如果迭代器函数返回一个promise或一个thenable,那么   在继续下一个之前,等待承诺的结果   迭代。

var Promise = require('bluebird'),
    _ = require('lodash'),
    allIPs = [];

connectToMaxMindDatabases().then(function (done){
    for(var i=0; i<256; i++){
        for(var j=0; j<256; j++){
            for(var k=0; k<256; k++){
                for(var l=0; l<256; l++){
                    count++;
                    if(count % 1000 == 0){
                        console.log("count", count);
                    }
                    var newIP = getIPV4([i,j,k,l])
                    allIPs.push(newIP);
                }
            }
        }
    }       
    return allIPs;
})
.then(function(allIPs) {
    // Create an array of arrays of IPs, each inner array with 1000 IPs
    var from = 0, to = 1000, segments = [], promises;

    do {
        segments.push(allIPs.slice(from, to));
        from += 1000; to += 1000;
    } while(to < allIPs.lenght);

    // Process a segment of 1000 IPs and wait before moving the next
    // segment until this segment is fully resolved.
    Promise.each(segments, function(segmentArr) {
        promises = [];
        _.forOwn(segmentArr, function(IP) {
            promises.push(ispDB.getGeoDataAsync(IP)
              .then(function(result) {
                  // save the result
              })
            );
        });
        return Primse.all(promises);
    });
});

当然,如果您的案例中的资源受到限制,allIPs数组甚至无法生成,因为它在此之前开始冻结,这将无法解决问题,但可能会被资源占用所有这些getGeoDataAsync次调用都应该有所帮助。