我在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!!");
}
};
答案 0 :(得分:0)
所以,这符合我的目的。但是缺点是什么呢?
如果你实际上有一个长时间运行的任务能够阻止事件循环,那么将它放在setTimeout()
上并不能阻止它阻止事件循环。这是缺点。它只是将事件循环阻塞从现在移动到事件循环的下一个滴答。无论哪种方式,事件循环都将被阻止相同的时间。
如果您在运行代码之前刚刚执行了res.json({status: "OK"})
,那么您将获得完全相同的结果。
如果长时间运行的代码(您描述为文件和数据库操作)实际上阻止了事件循环并且使用异步I / O操作正确编写,那么停止阻止事件循环的唯一方法是移动耗费大量CPU消耗node.js线程。
通常通过群集,将工作移动到工作进程或将工作移动到其他服务器来完成。您必须由另一个进程或另一个服务器完成此工作,以使其不受事件循环的影响。 setTimeout()
本身不会实现这一点。
child_process.spawn()
将实现这一目标。因此,如果您要解决实际的事件循环阻塞问题并且I / O已经尽可能地进行异步优化,那么将其移动到工作进程就是典型的node.js解决方案。您可以通过多种方式与该子流程进行沟通,但有一种可能性是stdin
和stdout
。