如何在node.js中的同一个进程中同步对文件的访问?

我有一个Web服务器,读取和写入磁盘上的数据文件。 我想要一个文件只写入一个Web请求。

这里有一个示例程序来说明我的问题。 它将状态文件保存在“/tmp/rw.txt”中,并在每次Web命中时递增整数内容。 运行这个程序,然后运行诸如ab -n 10000 -c 1000 http://localhost:3000/ ,表明通过多次命中从文件中读取相同的值,并且它被多次写入。

注意:我知道flock()和fs-ext 。 但是,flock()会将文件locking到当前进程; 因为这里所有的访问都在同一个进程中,flock()不起作用(这个例子相当复杂)。

另外请注意,我通常会使用express,async等来完成大部分工作。 只是为了举例而坚持基础。

 var http = require("http"), fs = require("fs"); var stateFile = "/tmp/rw.txt"; var server = http.createServer(function(req, res) { var writeNum = function(num) { var ns = num.toString(10); console.log("Writing " + ns); fs.writeFile(stateFile, ns, function(err) { if (err) { res.writeHead(500, {"Content-Type": "text/plain"}); res.end(err.message); } else { res.writeHead(200, {"Content-Type": "text/plain"}); res.end(ns); } }); }; switch (req.url) { case "/reset": writeNum(0); break; case "/": fs.readFile(stateFile, function(err, data) { if (err && err.code == "ENOENT") { // First time, set it to zero writeNum(0); } else if (err) { res.writeHead(500, {"Content-Type": "text/plain"}); res.end(err.message); } else { writeNum(parseInt(data, 10) + 1); } }); break; default: res.writeHead(404, {"Content-Type": "text/plain"}); res.end("No such resource: " + req.url); } }); server.listen(3000); 

将数据存储在文件中并不是Web服务器等多用户环境中的首选方式。 数据库更适合这个。 但如果你真的想坚持文件,我build议使用缓冲区。 也就是说,您要写入/读取的内存对象以及将其内容定期转储到磁盘的单独函数,如下所示:

 var server = http.createServer(function(req, res) { ----- cut ---- switch (req.url) { case "/reset": value = 0; break; case "/": value ++; break; default: res.writeHead(404, {"Content-Type": "text/plain"}); res.end("No such resource: " + req.url); } } ----- cut ---- } (function() { var cb = arguments.callee; fs.writeFile(stateFile, value, function(err) { // handle error setTimeout(cb, 100); // schedule next call }); })(); 

而另一种方法来做到这一点(如果有问题背后的东西,所以你不接受简单的解决scheme;))是创build一个处理队列。 下面是一个简单的队列模式,它按照它们提交的顺序执行请求,并在执行该函数后向所提供的callback返回错误(如果有的话)。

 var Queue = function(fn) { var queue = []; var processingFn = fn; var iterator = function(callback) { return function(err) { queue.shift(); // remove processed value callback(err); var next = queue[0]; if(next) processingFn(next.arg, iterator(next.cb)); }; } this.emit = function(obj, callback) { var empty = !queue.length; queue.push({ arg: obj, cb: callback}); if(empty) { // start processing processingFn(obj, iterator(callback)); } } } function writeNum(inc, continueCb) { fs.readFile(stateFile, function(err, data) { if(err) return continueCb(err); var value = data || 0; fs.writeFile(stateFile, value + inc, function(err) { continueCb(err); }); }); } var writer = new Queue(writeNum); // on each request writer.emit(1, function(err) { if(err) { // respond with error } else { // value written } }); 

我无法find一个我想要的图书馆,所以我在这里创build了一个:

https://npmjs.org/package/schlock

以上是使用读/写locking的上述示例程序。 我也用“步”来使整个事情更具可读性。

 var http = require("http"), fs = require("fs"), Schlock = require("schlock"), Step = require("step"); var stateFile = "/tmp/rw.txt"; var schlock = new Schlock(); var server = http.createServer(function(req, res) { var num; Step( function() { schlock.writeLock(stateFile, this); }, function(err) { if (err) throw err; fs.readFile(stateFile, this); }, function(err, data) { if (err && err.code == "ENOENT") { num = 0; } else if (err) { throw err; } else { num = parseInt(data, 10) + 1; } fs.writeFile(stateFile, num.toString(10), this); }, function(err) { if (err) throw err; schlock.writeUnlock(stateFile, this); }, function(err) { if (err) { res.writeHead(500, {"Content-Type": "text/plain"}); res.end(err.message); } else { res.writeHead(200, {"Content-Type": "text/plain"}); res.end(num.toString(10)); } } ); }); server.listen(3000); 

您可以使用fs.writeFileSync和readFileSync。

我刚刚通过npm searchfind的一个解决scheme是锁柜服务器: https : //github.com/bobrik/locker 。

我认为这是一个很好的解决scheme,这是关于如何devise的。

最大的问题是它处理一般情况(使用资源的多个进程),这需要一个单独的服务器。

我仍然在寻找一个关于同样事情的进程内解决scheme。