我正在运行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(".")
}
答案 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
次调用都应该有所帮助。