setTimeout还是child_process.spawn?

时间:2017-08-04 08:34:08

标签: node.js

我在Node.js中有一个REST服务,其中一个特定请求运行一堆数据库命令和其他文件处理,可能需要10-15秒才能运行。由于我不想保留我的浏览器请求线程,我编写了一个单独的.js脚本来执行需要的操作,在我的Node.js代码中使用child_process.spawn()调用脚本并立即返回OK客户。这样可以正常工作,但是通过使用简单的setTimeout调用相同的脚本(作为本地函数)也是如此。

router.post("/longRequest", function(req, res) {
          console.log("Started long request with id: " + req.body.id);
          var longRunningFunction = function() {
              // Usually runs a bunch of things that take time. 
              // Simulating a 10 sec delay for sample code.
              setTimeout(function() {
                  console.log("Done processing for 10 seconds")
              }, 10000);
          }
          // Below line used to be 
          // child_process.spawn('longRunningFunction.js'
          setTimeout(longRunningFunction, 0); 
          res.json({status: "OK"})
      })

所以,这符合我的目的。但是缺点是什么?我可能无法像child_process.spawn一样轻松监控离线过程,这会给我一个进程ID。但是,从长远来看,这是否会导致问题?如果10秒处理在未来增加到更多,它会阻止Node.js处理吗?

实际的longRunningFunction是读取Excel文件,解析它并使用繁琐的MS SQL Server进行批量加载的东西。

var XLSX    = require('xlsx');
var FileAPI = require('file-api'), File = FileAPI.File, FileList = FileAPI.FileList, FileReader = FileAPI.FileReader;
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var TYPES   = require('tedious').TYPES;
var importFile = function() {

  var file = new File(fileName); 

  if (file) {
    var reader = new FileReader();
    reader.onload = function (evt) {
        var data = evt.target.result;
        var workbook = XLSX.read(data, {type: 'binary'}); 
        var ws = workbook.Sheets[workbook.SheetNames[0]]; 
        var headerNames = XLSX.utils.sheet_to_json( ws, { header: 1 })[0];
        var data = XLSX.utils.sheet_to_json(ws);

        var bulkLoad = connection.newBulkLoad(tableName, function (error, rowCount) {
          if (error) {
             console.log("bulk upload error: " + error); 
          } else {
              console.log('inserted %d rows', rowCount);
          }
          connection.close();

        });

        // setup your columns - always indicate whether the column is nullable
        Object.keys(columnsAndDataTypes).forEach(function(columnName) {
            bulkLoad.addColumn(columnName, columnsAndDataTypes[columnName].dataType, { length: columnsAndDataTypes[columnName].len, nullable: true });
        })

        data.forEach(function(row) {
            var addRow = {}
            Object.keys(columnsAndDataTypes).forEach(function(columnName) {
                addRow[columnName] = row[columnName];
            })
            bulkLoad.addRow(addRow);
        })

        // execute
        connection.execBulkLoad(bulkLoad);
    };
    reader.readAsBinaryString(file);
  } else {
      console.log("No file!!");
  }
}; 

1 个答案:

答案 0 :(得分:0)

  

所以,这符合我的目的。但是缺点是什么呢?

如果你实际上有一个长时间运行的任务能够阻止事件循环,那么将它放在setTimeout()上并不能阻止它阻止事件循环。这是缺点。它只是将事件循环阻塞从现在移动到事件循环的下一个滴答。无论哪种方式,事件循环都将被阻止相同的时间。

如果您在运行代码之前刚刚执行了res.json({status: "OK"}),那么您将获得完全相同的结果。

如果长时间运行的代码(您描述为文件和数据库操作)实际上阻止了事件循环并且使用异步I / O操作正确编写,那么停止阻止事件循环的唯一方法是移动耗费大量CPU消耗node.js线程。

通常通过群集,将工作移动到工作进程或将工作移动到其他服务器来完成。您必须由另一个进程或另一个服务器完成此工作,以使其不受事件循环的影响。 setTimeout()本身不会实现这一点。

child_process.spawn()将实现这一目标。因此,如果您要解决实际的事件循环阻塞问题并且I / O已经尽可能地进行异步优化,那么将其移动到工作进程就是典型的node.js解决方案。您可以通过多种方式与该子流程进行沟通,但有一种可能性是stdinstdout