Javascript 如何使用异步I/O将实时数据集写入磁盘?
我不熟悉node.js的开发(虽然在客户端javascript方面比较有经验),在node.js中处理异步操作时,我遇到了很多关于良好实践的问题 我的具体问题(尽管我认为这是一个相当通用的主题)是,我有一个node.js应用程序(运行在Raspberry Pi上),它每10秒将几个温度探测器的读数记录到内存数据结构中。这个很好用。随着时间的推移,数据在内存中积累,当数据积累并达到特定的大小阈值时,数据会定期老化(仅保留最后N天的数据),以防止其增长超过特定的大小。此温度数据用于控制其他一些设备 然后,我有一个单独的时间间隔计时器,它每隔一段时间将这些数据写到磁盘上(如果进程崩溃,则将其持久化)。我正在使用async node.js(Javascript 如何使用异步I/O将实时数据集写入磁盘?,javascript,node.js,asynchronous,raspberry-pi,promise,Javascript,Node.js,Asynchronous,Raspberry Pi,Promise,我不熟悉node.js的开发(虽然在客户端javascript方面比较有经验),在node.js中处理异步操作时,我遇到了很多关于良好实践的问题 我的具体问题(尽管我认为这是一个相当通用的主题)是,我有一个node.js应用程序(运行在Raspberry Pi上),它每10秒将几个温度探测器的读数记录到内存数据结构中。这个很好用。随着时间的推移,数据在内存中积累,当数据积累并达到特定的大小阈值时,数据会定期老化(仅保留最后N天的数据),以防止其增长超过特定的大小。此温度数据用于控制其他一些设备
fs.open()
、fs.write()
和fs.close()
)磁盘IO将数据写入磁盘
而且,由于磁盘IO的异步性质,在我看来,我试图向磁盘写入的数据结构可能在我将其写入磁盘时得到修改。这可能是一件坏事。如果数据只在写入磁盘时被附加到数据结构上,这实际上不会导致我写数据的方式出现问题,但是在某些情况下,当新数据被记录时,更早的数据可以被修改,这将真正混淆我在写入磁盘的过程中的完整性。
我可以想出各种各样的有点难看的保护措施,比如:var dataQueue = async.queue(function(task, callback) {
if (task.type === "newData") {
memoryStore.add(task.data); // modify your data structure however you do it now
callback(); // let the queue know the task is done; you can pass an error here as usual if needed
} else if (task.type === "writeData") {
fs.writeFile(task.filename, JSON.stringify(memoryStore), function(err) {
// error handling
callback(err); // let the queue know the task is done
})
} else {
callback(new Error("Unknown Task")); // just in case we get a task we don't know about
}
}, 1); // The 1 here is setting the concurrency of the queue so that it will only run one task at a time
// call when you get new probe data
funcion addNewData(data) {
dataQueue.push({task: "newData", data: data}, function(err) {
// called when the task is complete; optional
});
}
// write to disk every 5 minutes
setInterval(function() {
dataQueue.push({task: "writeData", filename: "somefile.dat"}, function(err) {
// called when the task is complete; optional
});
}, 18000);
还请注意,现在可以将数据异步添加到数据结构中。假设您添加了一个新的探测器,每当事件的值发生变化时,它就会触发事件。您可以像处理现有探测一样addNewData(data)
,而不必担心它与正在进行的修改或磁盘写入冲突(如果您开始写入数据库而不是内存中的数据存储,那么这一点就很重要了)
更新:使用 其思想是使用
bind()
将参数绑定到函数,然后将bind()
返回的新绑定函数推送到队列中。这样,您就不需要将某个自定义对象推送到它必须解释的队列中;您只需给它一个要调用的函数,所有设置都已经有了正确的参数。唯一需要注意的是,函数必须将回调作为其最后一个参数
这应该允许您使用现有的所有函数(可能稍加修改),并在需要确保它们不会并发运行时将它们推送到队列中
我把这些放在一起测试这个概念:
var async = require('async');
var dataQueue = async.queue(function(task, callback) {
// task is just a function that takes a callback; call it
task(callback);
}, 1); // The 1 here is setting the concurrency of the queue so that it will only run one task at a time
function storeData(data, callback) {
setTimeout(function() { // simulate async op
console.log('store', data);
callback(); // let the queue know the task is done
}, 50);
}
function writeToDisk(filename, callback) {
setTimeout(function() { // simulate async op
console.log('write', filename);
callback(); // let the queue know the task is done
}, 250);
}
// store data every second
setInterval(function() {
var data = {date: Date.now()}
var boundStoreData = storeData.bind(null, data);
dataQueue.push(boundStoreData, function(err) {
console.log('store complete', data.date);
})
}, 1000)
// write to disk every 2 seconds
setInterval(function() {
var filename = Date.now() + ".dat"
var boundWriteToDisk = writeToDisk.bind(null, filename);
dataQueue.push(boundWriteToDisk, function(err) {
console.log('write complete', filename);
});
}, 2000);
首先,让我们展示一个实用的解决方案,然后让我们深入了解它的工作原理:
var chain = Promise.resolve(); // Create a resolved promise
var fs = Promise.promisifyAll(require("fs"));
chain = chain.then(function(){
return fs.writeAsync(...); // A
});
// some time in the future
chain = chain.then(function(){
return fs.writeAsync(...); // This will always execute after A is done
})
因为你已经用承诺来标记你的问题——值得一提的是,承诺能够很好地解决这个(相当复杂的)问题,而且很容易做到 您的数据同步问题称为问题。有很多方法可以解决JavaScript中的同步问题——这是一本关于这个主题的好书 输入:承诺 用承诺来解决这个问题的最简单的方法是通过一个承诺把所有的事情都联系起来。我知道你是
var fsQueue = Promise.resolve(); // start a new chain
// one place
fsQueue = fsQueue.then(function(){ // assuming promisified fs here
return fs.writeAsync(...);
});
// some other place
fsQueue = fsQueue.then(function(){
return fs.writeAsync(...);
});
var fs = B.promisifyAll(require("fs")); // bluebird promisified fs
var syncFs = { // sync stands for synchronized, not synchronous
queue: B.resolve();
writeAsync = function(){
var args = arguments
return (queue = queue.then( // only execute later
return fs.writeAsync.apply(fs,arguments);
});
} // promisify other used functions similarly
};
// assumes module is promisified and ignores nested functions
function synchronize(module){
var ret = {}, queue = B.resolve();
for(var fn in module){
ret[fn] = function(){
var args = arguments;
queue = queue.then(function(){
return module[fn].apply(module, args);
})
};
}
ret.queue = queue; // expose the queue for handling errors
return ret;
}