Node.js请求获取ETIMEDOUT和ESOCKETTIMEDOUT的模块

我正在爬取与请求模块的许多链接与asynchronous模块的组合并行。
我注意到ETIMEDOUTESOCKETTIMEDOUT错误的很多,虽然链接是可及的,并且使用chrome快速响应。

我已经将请求选项中的maxSockets限制为2, timeout为10000。 我使用限制为2的async.filterLimit() ,每次甚至将并行性降低到2个请求。 所以我有2个套接字,2个请求,10秒的超时等待从服务器的标题响应,但我得到这些错误。

这里的请求configuration我使用:

 { ... pool: { maxSockets: 2 }, timeout: 10000 , time: true ... } 

以下是我用于fecth链接的代码片段:

  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所以没有连接会因为缺less可用的套接字而挂断:

 pool: { maxSockets: Infinity } 

为了控制带宽,我实现了一个requestLoop方法,该方法用maxAttempsretryDelay参数处理请求来控制请求:

 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尝试克服套接字连接的超时错误 – 实现requestLoop方法来控制失败的请求和maxAttemps以及retryDelay这样的请求 – 也有最大数量的并发请求设置的传递数量到async.filterLimit

我想要注意的是,我也玩过这里的所有设置,以免错误爬行,但是迄今为止尝试失败。

仍然在寻求解决这个问题的帮助。

UPDATE2:我已经决定放弃async.filterLimit并创build我自己的限制机制。 我只有3个variables来帮助我实现这个目标:
pendingRequests – 将保存所有请求的请求数组(将在稍后解释) activeRequests – 活动请求数maxConcurrentRequests – 允许的最大并发请求数

到pendingRequests数组中,我推送一个包含对requestLoop函数的引用的复杂对象以及包含要传递给循环函数的参数的arguments数组:

 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达到activeRequests的限制的maxConcurrentRequests下pipe理请求和触发请求:

 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(); }); 

这应该解决任何超时错误,通过我的testing,我仍然注意到在我testing过的特定网站中有一些超时。 我不能100%确定这一点,但我想这是由于支持HTTP服务器的网站的性质,通过执行IP检查将用户请求限制到最大,并且因此返回一些HTTP 400消息以防止在服务器上可能的“攻击”。

编辑:复制https://stackoverflow.com/a/37946324/744276

默认情况下,Node有4名工作人员来parsingDNS查询 。 如果您的DNS查询需要很长时间,请求将在DNS阶段阻塞,症状正好是ESOCKETTIMEDOUTETIMEDOUT

尝试增加你的uv线程池大小:

 export UV_THREADPOOL_SIZE=128 node ... 

或者在index.js (或者任何你的入口点):

 #!/usr/bin/env node process.env.UV_THREADPOOL_SIZE = 128; function main() { ... } 

我通过使用tc减慢来自DNS服务器的响应,从而在本地重现了这一点 。

我发现如果有太多的asynchronous请求,那么ESOCKETTIMEDOUTexception发生在Linux中。 我find的解决方法是这样做的:

将此选项设置为request(): agent: false, pool: {maxSockets: 100}请注意,在此之后,超时可能在说谎,因此您可能需要增加它。