【发布时间】:2017-09-17 00:23:50
【问题描述】:
我正在使用 Node.js,我创建了一个简单的脚本,可以将文件从目录上传到服务器:
var request = require('request');
var file = require('file');
var fs = require('fs');
var path = require('path');
VERSION = '0.1'
CONFIG_FILE = path.join(__dirname, 'etc', 'sender.conf.json');
var config = JSON.parse(
fs.readFileSync(CONFIG_FILE).toString()
);
var DATA_DIR = __dirname
config['data_dir'].forEach(function(dir) {
DATA_DIR = path.join(DATA_DIR, dir)
});
console.log('sending data from root directory: ' + DATA_DIR);
file.walk(
DATA_DIR,
function(err, dir_path, dirs, files) {
if(err) {
return console.error(err);
}
sendFiles(dir_path, files);
}
);
function sendFiles(dir_path, files)
{
files
.filter(function(file) {
return file.substr(-5) === '.meta';
})
.forEach(function(file) {
var name = path.basename(file.slice(0, -5));
sendFile(dir_path, name);
})
;
}
function sendFile(dir_path, name)
{
console.log("reading file start: " + dir_path + "/" + name);
fs.readFile(
path.join(dir_path, name + '.meta'),
function(err, raw_meta) {
if(err) {
return console.error(err);
}
console.log("reading file done: " + dir_path + "/" + name);
sendData(
name,
JSON.parse(raw_meta),
fs.createReadStream(path.join(dir_path, name + '.data'))
);
}
);
console.log("reading file async: " + dir_path + "/" + name);
}
function sendData(name, meta, data_stream)
{
meta['source'] = config['data_source'];
var req = request.post(
config['sink_url'],
function(err, res, body) {
if(err) {
console.log(err);
}
else {
console.log(name);
console.log(meta);
console.log(body);
}
}
);
var form = req.form();
form.append(
'meta',
JSON.stringify(meta),
{
contentType: 'application/x-www-form-urlencoded'
}
);
form.append(
'data',
data_stream
);
}
当只使用几个文件运行时,它工作正常。但是当我在有很多文件的目录上运行它时,它会窒息。这是因为它不断创建大量任务以从文件中读取,但从未真正进行读取(因为文件太多)。这可以在输出中观察到:
sending data from root directory: .../data
reading file start: .../data/ac/ad/acigisu-adruire-sabeveab-ozaniaru-fugeef-wemathu-lubesoraf-lojoepe
reading file async: .../data/ac/ad/acigisu-adruire-sabeveab-ozaniaru-fugeef-wemathu-lubesoraf-lojoepe
reading file start: .../data/ac/ab/acodug-abueba-alizacod-ugvut-nucom
reading file async: .../data/ac/ab/acodug-abueba-alizacod-ugvut-nucom
reading file start: .../data/ac/as/acigisu-asetufvub-liwi-ru-mitdawej-vekof
reading file async: .../data/ac/as/acigisu-asetufvub-liwi-ru-mitdawej-vekof
reading file start: .../data/ac/av/ace-avhad-bop-rujan-pehwopa
reading file async: .../data/ac/av/ace-avhad-bop-rujan-pehwopa
...
对于每个文件,在调用fs.readFile 之前立即生成控制台输出"reading file start",并在安排异步读取之后立即生成"reading file async"。但是即使我让它运行很长时间也没有"reading file done" 消息,这意味着甚至可能从未计划过任何文件的读取(这些文件大约为 100 字节,因此一旦安排好,这些读取将可能一次完成)。
这使我想到了以下思考过程。 Node.js 中的异步调用已完成,因为事件循环本身是单线程的,我们不想阻止它。 但是,一旦满足此要求,将进一步的异步调用嵌套到本身嵌套在异步调用等中的异步调用中是否有意义?它会用于任何特定目的吗?此外,如果对单个文件的完整处理仅由同步调用组成,那么由于调度开销并不是真正需要并且可以完全避免的,这难道不是对代码的实际悲观吗?
考虑到上面的思考过程,我的做法是使用this question的解决方案:
- 异步推送所有文件名到
async.queue - 通过设置
queue.concurrency来限制并行任务的数量 - 提供完全同步的文件上传处理程序,即同步读取文件内容,完成后,同步向服务器发送POST请求
这是我第一次尝试使用 Node.js 和/或 JavaScript,因此我很可能完全错了(请注意,例如 sync-request package 非常清楚地表明同步调用是不可取的,这是矛盾的根据我上面的思考过程-问题是为什么)。任何关于上述思考过程的有效性以及所提出的解决方案的可行性和最终替代方案的评估都将非常感激。
【问题讨论】:
-
再想一想,我提出的解决方案并没有真正解决问题:我需要限制将文件添加到队列中,而不是处理文件(最好的方法是什么?) .不过,我会保持文件处理程序同步(异步调用,但不会自行产生更多异步任务)。
标签: node.js asynchronous