6

我有一个 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);
4

5 回答 5

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
    });
})();
于 2012-11-03T19:15:32.843 回答
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
  }
});
于 2012-11-03T19:40:43.440 回答
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);
于 2012-11-05T19:31:13.783 回答
1

您可以使用fs.writeFileSync和 readFileSync。

于 2012-11-03T04:16:17.793 回答
1

我刚刚找到的一种解决方案npm search是储物柜服务器:https ://github.com/bobrik/locker 。

我认为这是一个很好的解决问题的方法,它是关于我如何设计它。

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

我仍在寻找一个做同样事情的进程内解决方案。

于 2012-11-03T14:52:39.213 回答