Node.js的请求模块越来越ETIMEDOUT和ESOCKETTIMEDOUT
我爬行了大量的并行地与request模块与模块async组合链接。
我注意到ETIMEDOUT
和ESOCKETTIMEDOUT
错误的,尽管很多的链接访问,并使用镀铬相当迅速作出反应。
我已经限制了maxSockets
为2,在请求选项timeout
10000。我使用async.filterLimit()
用2到连并行减少到每次2要求的限制。所以,我有2个插槽,2个请求,并在10秒的超时等待头来自服务器的响应但我得到这些错误。
此处的请求的配置使用:
{
...
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
的连接对象是真/假所以有时它是一个连接超时,有时它不是(根据请求文档)
更新:我决定提振了maxSockets
到Infinity
所以没有连接将被挂断,由于缺乏可用的套接字:
pool: {
maxSockets: Infinity
}
在-为了控制带宽I实施了处理与一个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);
}
});
}
};
因此,这总结一下: - 提振maxSockets
到Infinity
尝试克服插座的连接超时错误 - Implemnted requestLoop
方法来控制失败的请求和maxAttemps
以及retryDelay
这样的请求 - 也有并发请求的最大信号数量过去了数设置到async.filterLimit
我想指出,我也玩的一切在这里,为了得到错误的免费检索,但到目前为止尝试失败的孔设置。
还在寻找关于解决此问题的帮助。
UPDATE2:我已经决定放弃async.filterLimit,让我自己的极限机制。我有3个变量来帮助我实现这一点:
pendingRequests
- 这将持有的所有请求的请求阵列(将在后面解释)activeRequests
- 活动请求的数目maxConcurrentRequests
- 允许的最大的数目的并发请求
入pendingRequests阵列,我推包含对requestLoop函数的引用以及含有该参数传递到循环函数的参数阵列一个复杂的对象:
self.pendingRequests.push({
"arguments": [{
uri: resource.uri.toString()
}, self.maxAttempts, function (error, response, body) {
if (!error) {
if (self.policyChecker.isMimeTypeAllowed((response.headers['content-type'] || '').split(';')[0]) &&
self.policyChecker.isFileSizeAllowed(body)) {
self.totalBytesFetched += body.length;
resource.content = self.decodeBuffer(body, response.headers["content-type"] || '', resource);
callback(null, resource);
} else {
self.fetchedUris.splice(self.fetchedUris.indexOf(resource.uri.toString()), 1);
callback(new Error('Fetch failed because a mime-type is not allowed or file size is bigger than permited'));
}
} else {
self.fetchedUris.splice(self.fetchedUris.indexOf(resource.uri.toString()), 1);
callback(error);
}
self.activeRequests--;
self.runRequest();
}],
"function": self.requestLoop
});
self.runRequest();
你“”注意在通话结束时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();
});
这应该解决任何超时错误,通过我的testings寿,我还是注意到一些超时在特定的网站我测试过此上。我不能100%地肯定这一点,但我想这是由于网站的支持HTTP的服务器做一个ip检查,因此限制用户请求最大返回的HTTP 400个邮件的性质为了防止服务器上的可能的“攻击”。
回答如下:编辑:https://stackoverflow/a/37946324/744276的重复
默认情况下,节点有4 workers to resolve DNS queries。如果你的DNS查询时间长上下的时间,请求将阻止对DNS阶段,症状是完全ESOCKETTIMEDOUT
或ETIMEDOUT
。
请试着提高UV线程池的大小:
export UV_THREADPOOL_SIZE=128
node ...
或index.js
(或任何你的切入点是):
#!/usr/bin/env node
process.env.UV_THREADPOOL_SIZE = 128;
function main() {
...
}
编辑:I also wrote a blog post了。