Node.js 错误:刮片时连接ETIMEDOUT

Node.js 错误:刮片时连接ETIMEDOUT,node.js,mongodb,express,rate-limiting,npm-request,Node.js,Mongodb,Express,Rate Limiting,Npm Request,我有一个函数: 1.从集合foo中的mongoDB文档获取3000个“id”属性的数组 2.为每个ID创建一个GET请求,以获取ID的“resp”obj,并将其存储在另一个数据库中 router.get('/', (req, res) => { var collection = db.get().collection('foo'); var collection2 = db.get().collection('test'); collection.distinct

我有一个函数:
1.从集合
foo
中的mongoDB文档获取3000个“id”属性的数组
2.为每个ID创建一个GET请求,以获取ID的“resp”obj,并将其存储在另一个数据库中

router.get('/', (req, res) => {

    var collection = db.get().collection('foo');
    var collection2 = db.get().collection('test');
    collection.distinct('id',  (err, idArr) => { // count: 3000+
    idArr.forEach(id => {
    let url = 'https://externalapi.io/id=' + id
    request(url, (error, response, body) => {
           if (error) { 
             console.log(error) 
           } else {
             resp = JSON.parse(resp);
             collection2.insert(resp);
           }
    });
});
节点错误日志:

[0] events.js:163
[0]       throw er; // Unhandled 'error' event
[0]       ^
[0]
[0] Error: connect ETIMEDOUT [EXT URL REDACTED]
[0]     at Object.exports._errnoException (util.js:1050:11)
[0]     at exports._exceptionWithHostPort (util.js:1073:20)
[0]     at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1093:14)
我使用简单的速率限制器不造成速率限制(25cps):

但在300-1700个请求之间的任何地方,我都会收到这个错误,它会使命令行上的节点崩溃。 如何处理此错误以防止我的应用程序崩溃


我已经尝试了很多错误处理,但没有一个能够处理
connect-ETIMEDOUT

,如评论中所述,如果您想控制同时进行的最大请求数,您可以使用Bluebird相当容易地做到这一点,如下所示:

const Promise = require('bluebird');
const rp = require('request-promise');

router.get('/', (req, res) => {

    let collection = db.get().collection('foo');
    let collection2 = db.get().collection('test');
    collection.distinct('id', (err, idArr) => { // count: 3000+
        if (err) {
            // handle error here, send some error response
            res.status(501).send(...);
        } else {
            Promise.map(idArr, id => {
                let url = 'https://externalapi.io/id=' + id
                return rp(url).then(body => {
                    if (error) {
                        console.log(error)
                    } else {
                        let resp = JSON.parse(body);
                        // probably want to return a promise here too, but I'm unsure what DB you're using
                        collection2.insert(resp);
                    }
                }).catch(err => {
                    // decide what you want to do when a single request fails here
                    // by providing a catch handler that does not rethrow, other requests will continue
                });
                   // pick some concurrency value here that does not cause errors
            }, {concurrency: 10}).then(() => {
                // all requests are done, send final response
                res.send(...);
            }).catch(err => {
                // your code may never get here (depends upon earlier .catch() handler)
            });
        }
    });
});

如评论中所述,如果您想控制同时进行的最大请求数,您可以使用Bluebird相当轻松地做到这一点,如下所示:

const Promise = require('bluebird');
const rp = require('request-promise');

router.get('/', (req, res) => {

    let collection = db.get().collection('foo');
    let collection2 = db.get().collection('test');
    collection.distinct('id', (err, idArr) => { // count: 3000+
        if (err) {
            // handle error here, send some error response
            res.status(501).send(...);
        } else {
            Promise.map(idArr, id => {
                let url = 'https://externalapi.io/id=' + id
                return rp(url).then(body => {
                    if (error) {
                        console.log(error)
                    } else {
                        let resp = JSON.parse(body);
                        // probably want to return a promise here too, but I'm unsure what DB you're using
                        collection2.insert(resp);
                    }
                }).catch(err => {
                    // decide what you want to do when a single request fails here
                    // by providing a catch handler that does not rethrow, other requests will continue
                });
                   // pick some concurrency value here that does not cause errors
            }, {concurrency: 10}).then(() => {
                // all requests are done, send final response
                res.send(...);
            }).catch(err => {
                // your code may never get here (depends upon earlier .catch() handler)
            });
        }
    });
});


我将把您链接到我之前的回答:您同时向同一主机运行多少个请求?我的猜测是,您运行的数据太多,这对您的服务器或您试图从中获取数据的主机来说都是一个问题。ETIMEDOUT意味着某些东西无法跟上您的速度。您必须降低请求的数量,增加相关的超时时间,或者两者兼而有之。我们需要更多关于错误发生位置的信息,以了解如何更优雅地处理错误。仅供参考,发送请求太快,因此导致错误,记录哪些事情失败,然后稍后再尝试对任何人都不好。这可能不是您的最大吞吐量,故意在系统容量之外运行系统并强制出错可能会导致其他问题。我不熟悉该速率限制器。主机有无数种方法来决定实施速率限制。简单请求/秒只是一种方式。您可以每秒只发送10个请求,但如果某些请求的处理时间超过0.1秒,则可能会累积数百个打开的请求,这可能会触发限制。我发现的最安全的方法是限制您在同一时间的飞行中有多少个请求,而不仅仅是它们发送的速度。我喜欢使用Bluebird的
Promise.map()
来实现该功能,但如果您愿意,也可以手动编写代码。这里有一个方案可以控制同时有多少个请求处于“运行中”:以及。我会将您链接到我以前的答案:您同时有多少个请求运行到同一台主机?我的猜测是,您运行的数据太多,这对您的服务器或您试图从中获取数据的主机来说都是一个问题。ETIMEDOUT意味着某些东西无法跟上您的速度。您必须降低请求的数量,增加相关的超时时间,或者两者兼而有之。我们需要更多关于错误发生位置的信息,以了解如何更优雅地处理错误。仅供参考,发送请求太快,因此导致错误,记录哪些事情失败,然后稍后再尝试对任何人都不好。这可能不是您的最大吞吐量,故意在系统容量之外运行系统并强制出错可能会导致其他问题。我不熟悉该速率限制器。主机有无数种方法来决定实施速率限制。简单请求/秒只是一种方式。您可以每秒只发送10个请求,但如果某些请求的处理时间超过0.1秒,则可能会累积数百个打开的请求,这可能会触发限制。我发现的最安全的方法是限制您在同一时间的飞行中有多少个请求,而不仅仅是它们发送的速度。我喜欢使用Bluebird的
Promise.map()
来实现该功能,但如果您愿意,也可以手动编写代码。以下是一个方案,用于控制同时有多少请求处于“运行中”:以及。下面是一个场景:管理员仪表板中的管理员单击一个按钮,该按钮显示“更新数据库”,单击该按钮时,这条路线会被触发。现在,当所有的工作都在后台完成时,我可以立即发送一个响应“ok”。。。我怎样才能得到确认答复?i、 e.“完成”或在完成所有操作后向客户端发送响应(等待此响应可能需要>10分钟。)@Moshe-浏览器不喜欢等待10分钟的HTTP响应。它可能可以通过两端的各种技巧来完成。一种方法是让客户端连接webSocket或socket.io连接,您立即给出HTTP响应,然后在webSocket/socket.io连接上传递进度。您将从
.insert()
操作返回承诺。这将与您的
.insert()
操作同步,这样您就不会过载本地数据库,因此如果有任何错误,您也会看到它们。@Moshe-如果您使用的是高于2.0.36的MongoDB版本,那么承诺是内置的。您只需执行
返回集合2.插入(resp)
(如果我对文档的读取是正确的)。这里的例子:.@Moshe-我在我的回答中删除了一个额外的
输入错误
返回rp(url)。然后(body=>{…})
。下面是一个场景:管理仪表板中的管理员单击一个显示“更新数据库”的按钮,当单击此按钮时,此路由被触发。现在,当所有的工作都在后台完成时,我可以立即发送一个响应“ok”。。。我怎样才能得到确认答复?i、 e.“完成”或在完成所有操作后向客户端发送响应(等待此响应可能需要>10分钟。)@Moshe-浏览器不喜欢等待10分钟的HTTP响应。它可能可以通过两端的各种技巧来完成。一个想法是让客户端连接webSocket或socket.io连接,然后您提供immedia