【问题标题】:Node JS request blocking other requestsNode JS 请求阻塞其他请求
【发布时间】:2017-11-17 15:53:23
【问题描述】:

我创建了 Node JS 应用程序,我明确地从数据库下载了超过 100000 条记录。当请求正在进行时,我尝试从另一个浏览器登录同一个应用程序,除非前一个请求完成,否则它不会响应。任何想法 ?与事件循环或线程有什么关系? 所以这是我的逻辑。 我在步骤 1 中向我的 API 发出 get 请求,API 在步骤 2 中调用数据库层。数据库仅返回 21 条记录,我明确循环 100000*21 以进行大量渲染,只是为了测试 json2csv 上的负载。这样做时,对服务器的其他请求将不会响应,直到最后一个处理完成。

Step 1:

router.get('/report/downloadOverdueTrainings/:criteria', function (req, res, next) {
        var overDueTrainings = [];
        var reportManager = new ReportManager();
        var result = reportManager.getOverdueTrainings(JSON.parse(req.params.criteria));
        result.then(function (result) {
            var fields = ['Over Due Trainings'];
            for (var i = 0; i < 100000; i++) { //Testing purpose
                for (var training of result) {
                    overDueTrainings.push({
                        'Over Due Trainings': training.OverDueTrainings
                    })
                }
            }
            json2csv({
                data: overDueTrainings,
                fields: fields
            }, function (err, csv) {
                if (err)
                    throw err;
                res.setHeader('Content-disposition', 'attachment; filename=OverdueTrainings.csv');
                res.set('Content-Type', 'text/csv');
                res.send(csv);
            });
        }).catch(function (err) {
            next(err);
        });
    });

Step 2: Database Logic
var xtrDatabaseConnection =require('./xtrDatabaseConnection').XTRDatabaseConnection;
       ReportData.prototype.getOverdueTrainings = async function (params) {
        var connection = new xtrDatabaseConnection();
        var sequelize = connection.getSequelize();
        try {
            var query = "CALL report_getOverdueTrainings(:p_CourseCode,:p_Revision,:p_RevisionNo,:p_UserGroup,:p_Username,:p_Status,:p_SortColoumnNo,:p_SortColoumnDirection,:p_callType,:p_StartIndex,:p_PageSize)";
            var replacements = {
                p_CourseCode: params.CourseCode,
                p_Revision: params.Revision,
                p_RevisionNo: (params.RevisionNo == '' || params.RevisionNo == null) ? 0 : params.RevisionNo,
                p_UserGroup: params.UserGroup,
                p_Username: params.Username,
                p_Status: params.Status,
                p_SortColoumnNo: params.SortColoumnNo,
                p_SortColoumnDirection: params.SortColoumnDirection,
                p_callType: params.callType,
                p_StartIndex: params.startIndex,
                p_PageSize: params.pageSize
            };
            //console.log(replacements);
            return await connection.executeQuerySequelize(sequelize, query, Sequelize.QueryTypes.RAW, replacements);
        } catch (e) {
            throw (e);
        } finally {
            //To close connections
            sequelize.connectionManager.close().then(() => console.log('Connection Closed'));
        }
    }




XTRDatabaseConnection.prototype.executeQuerySequelize = function (sequelize, query, queryType, replacements) {
    return sequelize.authenticate()
        .then(() => {
            return sequelize.query(query, {
                replacements: replacements,
                type: queryType
            }).
            then(result => result)
                .catch(err => {
                    throw (err);
                });
        })
        .catch(err => {
            xtrUtility.logErrorsToWinstonTransports('Unable to connect to the database or some error occurred while executing your query. ', err);
            throw new AppError(err, "dbError", "Unable to connect to the database or some error occurred while executing your query.");
        });
}

【问题讨论】:

  • 你遇到了什么错误
  • 我没有收到任何错误,当我生成下载 csv 文件的请求时,同时执行任何其他功能在文件下载之前它不会响应。这意味着有东西阻塞了。
  • 因为for循环阻塞了事件循环,你可以尝试使用集群或子进程
  • 你用的是快递吗?
  • 完成数据库任务需要多长时间?如果完成,您的登录是否正常?

标签: javascript node.js promise mean-stack es6-promise


【解决方案1】:

NodeJS 是单线程、单进程。只要 javascript 函数正在运行,其他任何东西都无法运行。这是设计使然。只有在您的函数停止执行后,事件循环才会再次启动。

【讨论】:

  • 我完全否认了你对问题的回答。 node.js 中有很多方法可以做到这一点。虽然我同意你写的。
  • 没有真正的问题,但我把它当作 OP 询问它为什么被阻塞。我认为对于任何有抱负的 node.js 开发人员来说,在着手解决方法之前了解 javascript 事件循环很重要。
【解决方案2】:

这是阻塞:

    for (var i = 0; i < 100000; i++) { //Testing purpose
        for (var training of result) {
            overDueTrainings.push({
                'Over Due Trainings': training.OverDueTrainings
            })
        }
    }

直到执行完成。

还有

 var result = reportManager.getOverdueTrainings(JSON.parse(req.params.criteria));
    result.then(function (result) {
 /****/
 return await connection.executeQuerySequelize(sequelize, query, Sequelize.QueryTypes.RAW, replacements);

【讨论】:

  • 不知道为什么这被否决了,因为它确实是阻塞的,并且在这个循环运行时 node.js 进程中没有其他 Javascript(包括任何其他事件或请求)可以运行。
  • 那么我如何测试这个作为循环的替代方法??
  • @jfriend00 是的,不知道为什么,你是对的。 @saad-zulfiqar 您应该将测试 blocking 部分放入真正阻塞的后端
【解决方案3】:

Node.js 在单线程上工作。因此,您需要将它用于带有异步调用的小型进程。当你使用 async/await 时,你会阻塞回调函数。

所以你需要使用你的数据库函数作为承诺。它将异步工作。

.query('CALL someFunction()')
.then(function(response){
      //done
    }).error(function(err){
     //error
});

但是有一个问题。您需要加载 21 倍的结果。所以在这种情况下你需要使用递归函数。完成递归调用后,您可以将数据异步发送到客户端。

【讨论】:

    猜你喜欢
    • 2015-08-25
    • 1970-01-01
    • 2015-09-11
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多