当前位置: 首页 > 知识库问答 >
问题:

节点。js请求模块获取ETIMEDOUT和ESOCKETTIMEDOUT

汪耀
2023-03-14

我用请求模块和异步模块的组合并行爬行了很多链接
我注意到很多ETIMEDOUTESOCKETTIMEDOUT错误,尽管使用chrome可以访问链接并快速响应。

在请求选项中,我将maxSocket限制为2,将timeout限制为10000。我使用限制为2的async.filterLimited()来减少每次请求的并行性。所以我有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方法,该方法使用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);
            }
        });
    }
};

所以总结一下:-提升maxSocketsInfinity来尝试克服套接字连接的超时错误-实现请求循环方法来控制失败的请求和maxAttemps以及retry Delay>的此类请求-还有最大并发请求数由传递给的数量设置async.filter限制

我想指出,我也玩过这里的一切设置,以便获得错误的自由爬行,但到目前为止,尝试也失败了。

仍在寻求有关解决此问题的帮助。

更新2:我决定放弃异步。过滤器限制,并使我自己的限制机制。我只有3个变量来帮助我实现这一点:
pendingRequests-一个包含所有请求的请求数组(稍后将解释)activeRequests-活动请求数maxConcurrentRequests-允许的最大并发请求数

在pendingRequests数组中,我推送一个复杂对象,其中包含对requestLoop函数的引用,以及包含要传递给loop函数的参数的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限制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消息,以防止服务器上可能发生的“攻击”。

共有2个答案

阎鸿煊
2023-03-14

我发现如果有太多的异步请求,那么ESOCKETTIMEDOUT异常发生在linux。我找到的变通方法是这样做的:

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

令狐翰
2023-03-14

编辑:副本https://stackoverflow.com/a/37946324/744276

默认情况下,节点有4个工作进程来解析DNS查询。如果您的DNS查询需要很长时间,请求将在DNS阶段被阻止,症状就是ESOCKETTIMEDOUTETIMEDOUT

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

export UV_THREADPOOL_SIZE=128
node ...

或者在索引中。js(或您的入口点在哪里):

#!/usr/bin/env node
process.env.UV_THREADPOOL_SIZE = 128;

function main() {
   ...
}

编辑1:我也写了一篇关于它的博客文章。

编辑2:如果查询是非唯一的,您可能希望使用缓存,如nscd。

 类似资料:
  • 我正在尝试在后端节点中发出http请求。js web app。我可以在前端web javascript文件上发出成功的完整http请求,如下所示: 并遵循本指南:https://nodejs.dev/learn/make-an-http-post-request-using-nodejs 我一直试图在node中发出相同的请求。js后端: 但运行上述请求会导致以下错误: 我设置url/path设置的

  • 我在循环中的节点JS中发送这样的GET请求 由于响应是异步的,是否可以在响应中获取原始请求URL? 谢谢!

  • 问题内容: 有人可以解释如何使用request.js池哈希吗? 在github上的注释说,这大约池: 池-包含这些请求的代理的哈希对象。如果省略,该请求将使用设置为节点的默认maxSockets的全局池。 pool.maxSockets-包含池中最大套接字数量的整数。 我有用于编写CouchDB实例的代码(请注意问号)。基本上,任何连接到我的Node服务器的用户都将彼此独立地写入数据库: 什么是高

  • 这是我的Node Express代码, 但我将请求正文作为 {} 但在我的网络选项卡在Chrome我可以看到请求有效负载。请注意选项在此POST调用之前被激发。 请求标头 POST/abchttp/1.1 host:localhost:8112连接: 保持活动内容-长度:11 pragma:no-cache cache-control:no-cache 来源:http://localhost:42

  • 一个星期以来,我一直在解决这个问题;我连接到我的服务器API并检索cookie。 然后我做了一个GET来检索JSON Cookie会话,但是我在跨源代码方面遇到了一个问题。 在API的一侧是可以的,因为我得到了一个良好的状态200。 答复如下: ============================================================= 响应:访问控制允许信任。。。真

  • 我正在用nodejs和MongoDB创建我的第一个API,我想创建一个类似“localhost:3050/user/name”的GET请求。所以我建立了这个: ...