我有一个Web服务器,可以读写磁盘上的数据文件。我想只在一个Web请求中写一个文件。
这是一个说明我的问题的示例程序。它将状态文件保存在“/tmp/rw.txt”中,并在每次Web命中时增加整数内容。运行此程序,然后运行ab -n 10000 -c 1000 http://localhost:3000/
之类的程序,表明通过多次点击从文件中读取相同的值,并且多次写入。
注意:我知道flock()和fs-ext。但是,flock()会将文件锁定到当前进程;因为这里的所有访问都在同一个进程中,所以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);
答案 0 :(得分:2)
在Web服务器等多用户环境中,将数据存储在文件中不是首选方式。数据库更适合这种情况。但如果你真的想坚持使用文件,我建议使用缓冲区。也就是说,您编写/读取的内存对象,以及定期将其内容转储到磁盘的单独函数,如下所示:
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
});
})();
答案 1 :(得分:2)
另一种方法(如果你的问题背后有什么东西让你不接受简单的解决方案;))就是创建一个处理队列。下面是一个简单的队列模式,它按照提交的顺序执行请求,并在执行函数后将错误(如果有的话)返回给提供的回调。
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
}
});
答案 2 :(得分:2)
我无法找到满足我想要的库,所以我在这里创建了一个:
https://npmjs.org/package/schlock
以上是使用读/写锁定的上述示例程序。我还使用“Step”来使整个事物更具可读性。
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);
答案 3 :(得分:1)
您可以使用fs.writeFileSync和readFileSync。
答案 4 :(得分:1)
我刚通过npm search
找到的一个解决方案是更衣室服务器:https://github.com/bobrik/locker。
我认为这是解决问题的一个很好的解决方案,而且是关于我如何设计它的。
最大的问题是它处理一般情况(使用资源的多个进程),这需要一个单独的服务器。
我仍然在寻找能够做同样事情的进程内解决方案。