Node.js请求模块获取ETIMEDOUT和ESOCKETTIMEDOUT
我用模块和模块的组合并行爬行了很多链接。Node.js请求模块获取ETIMEDOUT和ESOCKETTIMEDOUT,node.js,sockets,asynchronous,request,httprequest,Node.js,Sockets,Asynchronous,Request,Httprequest,我用模块和模块的组合并行爬行了很多链接。 我注意到了很多ETIMEDOUT和ESOCKETTIMEDOUT错误,尽管使用chrome可以访问链接并快速响应 我已将请求选项中的maxSockets限制为2,将timeout限制为10000。 我使用的async.filterLimit()限制为2,甚至可以将并行度降低到每次2个请求。 因此,我有2个套接字、2个请求和10秒的超时等待来自服务器的头响应,但我得到了这些错误 在这里;s我使用的请求配置: { ... pool: {
我注意到了很多
ETIMEDOUT
和ESOCKETTIMEDOUT
错误,尽管使用chrome可以访问链接并快速响应
我已将请求选项中的maxSockets
限制为2,将timeout
限制为10000。
我使用的async.filterLimit()
限制为2,甚至可以将并行度降低到每次2个请求。
因此,我有2个套接字、2个请求和10秒的超时等待来自服务器的头响应,但我得到了这些错误
在这里;s我使用的请求配置:
{
...
pool: {
maxSockets: 2
},
timeout: 10000
,
time: true
...
}
下面是我用来创建链接的代码片段:
var self = this;
async.filterLimit(resources, 2, function(resource, callback) {
request({
uri: resource.uri
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
...
} else {
self.emit('error', resource, error);
}
callback(...);
})
}, function(result) {
callback(null, result);
});
我听了错误事件,我看到每当错误代码为ETIMEDOUT
时,connect对象为true/false,因此有时是连接超时,有时不是(根据请求文档)
更新:
我决定将maxSockets
提升到Infinity
,这样就不会因为缺少可用的套接字而中断连接:
pool: {
maxSockets: Infinity
}
为了控制带宽,我实现了一种requestLoop
方法,该方法使用maxAttemps
和retryDelay
参数处理请求,以控制请求:
async.filterLimit(resources, 10, function(resource, callback) {
self.requestLoop({
uri: resource.uri
}, 100, 5000, function (error, response, body) {
var fetched = false;
if (!error) {
...
} else {
....
}
callback(...);
});
}, function(result) {
callback(null, result);
});
requestLoop的实现:
requestLoop = function(options, attemptsLeft, retryDelay, callback, lastError) {
var self = this;
if (attemptsLeft <= 0) {
callback((lastError != null ? lastError : new Error('...')));
} else {
request(options, function (error, response, body) {
var recoverableErrors = ['ESOCKETTIMEDOUT', 'ETIMEDOUT', 'ECONNRESET', 'ECONNREFUSED'];
var e;
if ((error && _.contains(recoverableErrors, error.code)) || (response && (500 <= response.statusCode && response.statusCode < 600))) {
e = error ? new Error('...');
e.code = error ? error.code : response.statusCode;
setTimeout((function () {
self.requestLoop(options, --attemptsLeft, retryDelay, callback, e);
}), retryDelay);
} else if (!error && (200 <= response.statusCode && response.statusCode < 300)) {
callback(null, response, body);
} else if (error) {
e = new Error('...');
e.code = error.code;
callback(e);
} else {
e = new Error('...');
e.code = response.statusCode;
callback(e);
}
});
}
};
您“”注意到末尾调用了runRequest()
。
此功能任务是在最大activeRequests
限制maxConcurrentRequests
的同时,管理请求并在可能的情况下激发请求:
var self = this;
process.nextTick(function() {
var next;
if (!self.pendingRequests.length || self.activeRequests >= self.maxConcurrentRequests) {
return;
}
self.activeRequests++;
next = self.pendingRequests.shift();
next["function"].apply(self, next["arguments"]);
self.runRequest();
});
这应该可以解决任何超时错误,通过我的测试,我仍然注意到在我测试过的特定网站上有一些超时。我不能100%确定这一点,但我认为这是由于支持http服务器的网站的性质,通过执行ip检查,将用户请求限制在最大值,并因此返回一些http 400消息,以防止可能对服务器的“攻击”。编辑:重复
默认情况下,节点具有。如果您的DNS查询需要很长时间,请求将在DNS阶段被阻止,症状就是ESOCKETTIMEDOUT
或ETIMEDOUT
尝试增加uv线程池大小:
export UV_THREADPOOL_SIZE=128
node ...
或者在index.js
中(或者您的入口点在哪里):
编辑1:关于它
编辑2:如果查询是非唯一的,您可能需要使用缓存,如。我发现如果异步请求太多,则在linux中会发生ESOCKETTIMEDOUT异常。我发现的解决方法是: 将此选项设置为request():
代理:false,池:{maxSockets:100}
请注意,在此之后,超时时间可能会延迟,因此您可能需要增加超时时间 你有没有弄明白@Jorayen?@DvideBy0更新了一个解决方案我发现我的代码有点不同。就底层套接字而言,似乎没有考虑在代码中设置超时。我必须等待套接字事件(见下文)。on('socket',function(socket){socket.setTimeout(30000);socket.on('timeout',function(){request.abort();logger.error('request timeout out out out out after 30秒');};})感谢您对本主题的有益补充,我还没有测试您的建议,但知道这一点很好。我也会在有空的时候报告结果:)我读到的关于UV_THREADPOOL_SIZE的内容表明,这对于阻止io(如磁盘访问)最为重要,但对于非阻止io(如网络访问)则无关紧要。这是正确的,除了DNS解析也会被阻止,因为
getaddrinfo(3)
有效。首先,这对我来说很有效,因为我下载了几百个小文件,导致了这个错误。第二,增加插座数量的缺点是什么?它们会在某个时候自动关闭吗?
export UV_THREADPOOL_SIZE=128
node ...
#!/usr/bin/env node
process.env.UV_THREADPOOL_SIZE = 128;
function main() {
...
}