Javascript Node.js获取请求ETIMEDOUT&;埃索克提米德酒店
我正在使用Node.js-async&request模块抓取1亿多个网站,几分钟后我不断遇到错误Javascript Node.js获取请求ETIMEDOUT&;埃索克提米德酒店,javascript,node.js,node-request,Javascript,Node.js,Node Request,我正在使用Node.js-async&request模块抓取1亿多个网站,几分钟后我不断遇到错误ESOCKETTIMEDOUT&ETIMEDOUT 重新启动脚本后,它会再次工作。这似乎不是连接限制问题,因为我仍然可以毫不延迟地执行resolve4、resolveNs、resolveMx和curl 你认为代码有什么问题吗?或者有什么建议?我想将async.queue()并发性提高到至少1000。多谢各位 var request = require('request'), async = r
ESOCKETTIMEDOUT
&ETIMEDOUT
重新启动脚本后,它会再次工作。这似乎不是连接限制问题,因为我仍然可以毫不延迟地执行resolve4、resolveNs、resolveMx和curl
你认为代码有什么问题吗?或者有什么建议?我想将async.queue()并发性提高到至少1000。多谢各位
var request = require('request'),
async = require('async'),
mysql = require('mysql'),
dns = require('dns'),
url = require('url'),
cheerio = require('cheerio'),
iconv = require('iconv-lite'),
charset = require('charset'),
config = require('./spy.config'),
pool = mysql.createPool(config.db);
iconv.skipDecodeWarning = true;
var queue = async.queue(function (task, cb) {
dns.resolve4('www.' + task.domain, function (err, addresses) {
if (err) {
//
// Do something
//
setImmediate(function () {
cb()
});
} else {
request({
url: 'http://www.' + task.domain,
method: 'GET',
encoding: 'binary',
followRedirect: true,
pool: false,
pool: { maxSockets: 1000 },
timeout: 15000 // 15 sec
}, function (error, response, body) {
//console.info(task);
if (!error) {
// If ok, do something
} else {
// If not ok, do these
console.log(error);
// It keeps erroring here after few minutes, resolve4, resolveNs, resolveMx still work here.
// { [Error: ETIMEDOUT] code: 'ETIMEDOUT' }
// { [Error: ESOCKETTIMEDOUT] code: 'ESOCKETTIMEDOUT' }
var ns = [],
ip = [],
mx = [];
async.parallel([
function (callback) {
// Resolves the domain's name server records
dns.resolveNs(task.domain, function (err, addresses) {
if (!err) {
ns = addresses;
}
callback();
});
}, function (callback) {
// Resolves the domain's IPV4 addresses
dns.resolve4(task.domain, function (err, addresses) {
if (!err) {
ip = addresses;
}
callback();
});
}, function (callback) {
// Resolves the domain's MX records
dns.resolveMx(task.domain, function (err, addresses) {
if (!err) {
addresses.forEach(function (a) {
mx.push(a.exchange);
});
}
callback();
});
}
], function (err) {
if (err) return next(err);
// do something
});
}
setImmediate(function () {
cb()
});
});
}
});
}, 200);
// When the queue is emptied we want to check if we're done
queue.drain = function () {
setImmediate(function () {
checkDone()
});
};
function consoleLog(msg) {
//console.info(msg);
}
function checkDone() {
if (queue.length() == 0) {
setImmediate(function () {
crawlQueue()
});
} else {
console.log("checkDone() not zero");
}
}
function query(sql) {
pool.getConnection(function (err, connection) {
if (!err) {
//console.log(sql);
connection.query(sql, function (err, results) {
connection.release();
});
}
});
}
function crawlQueue() {
pool.getConnection(function (err, connection) {
if (!err) {
var sql = "SELECT * FROM domain last_update < (UNIX_TIMESTAMP() - 2592000) LIMIT 500";
connection.query(sql, function (err, results) {
if (!err) {
if (results.length) {
for (var i = 0, len = results.length; i < len; ++i) {
queue.push({"id": results[i]['id'], "domain": results[i]['domain'] });
}
} else {
process.exit();
}
connection.release();
} else {
connection.release();
setImmediate(function () {
crawlQueue()
});
}
});
} else {
setImmediate(function () {
crawlQueue()
});
}
});
}
setImmediate(function () {
crawlQueue()
});
系统控制
net.ipv4.ip_local_port_range = 10000 61000
默认情况下,节点具有。如果您的DNS查询需要很长时间,请求将在DNS阶段被阻止,症状就是ESOCKETTIMEDOUT
或ETIMEDOUT
尝试增加uv线程池大小:
export UV_THREADPOOL_SIZE=128
node ...
或者在index.js
中(或者您的入口点在哪里):
编辑:关于它。我也有同样的问题。读取后,通过在请求选项中使用“agent:false”来解决此问题 2017年10月31日 上述最初的回答似乎不能完全解决问题。我们找到的最终解决方案是在代理中使用keepAlive选项。例如:
var pool = new https.Agent({ keepAlive: true });
function getJsonOptions(_url) {
return {
url: _url,
method: 'GET',
agent: pool,
json: true
};
}
节点的默认池似乎默认为keepAlive=false,这会导致在每个请求上创建一个新连接。如果在短时间内创建了太多连接,则会出现上述错误。我的猜测是,服务路径上的一个或多个路由器阻塞了连接请求,可能是因为怀疑存在拒绝服务攻击。无论如何,上面的代码示例完全解决了我们的问题。在请求工具()
默认情况下,http连接保持活动状态处于关闭状态
您需要设置option.forever=true以打开此功能。TL:DR在循环之前,仅使用您的设置配置一次
request
的包装器,并使用forever:true
设置
const customRequest=request.defaults({forever:true});//包装纸
customRequest({uri:uri});
详细的回答我在循环中执行请求时遇到了完全相同的问题。然而,以上这些对我都不起作用 要在一定时间后停止对某些请求获取
ETIMEDOUT
和ESOCKETTIMEDOUT
,请执行以下操作:
request
设置。相反,在循环之前只使用设置创建一次请求
包装。作为缔约国:{maxSockets:Infinity}
配置池时,我仍然面临错误解决我问题的唯一配置是永远:true
。这将使已建立的连接保持活动状态const request=require('request');
//定制包装
const customRequest=request.defaults({
永远:真的,
超时:20000,
编码:空
})
循环(url,(url)=>{//对于我的每个url
customRequest({uri:url},(err,res,body)=>{
控制台日志(“完成”);
});
});
使用这种策略,我能够以每秒25个请求的速度处理大约400K个请求,而在处理过程中没有任何问题(Ubuntu 18.04 VM,4GB RAM,默认UV_线程池大小)。为什么池(请求时)设置两次?这是为了禁用池。无论有没有池和maxSockets,我仍然会出现错误。您能找到原因吗?请参阅我的答案以获得解决方案:我也有同样的问题,但此解决方案不适用于我。有什么想法吗?这意味着要么问题在别处,要么你正在耗尽你的128个线程。在后一种情况下,您需要以本机方式进行DNS解析——避免
getaddrinfo(3)
。
#!/usr/bin/env node
process.env.UV_THREADPOOL_SIZE = 128;
function main() {
...
}
var pool = new https.Agent({ keepAlive: true });
function getJsonOptions(_url) {
return {
url: _url,
method: 'GET',
agent: pool,
json: true
};
}