6

ディスク上のデータ ファイルを読み書きする Web サーバーがあります。単一の Web 要求でのみファイルに書き込みたいと思います。

これが私の問題を説明するサンプルプログラムです。「/tmp/rw.txt」に状態ファイルを保持し、Web ヒットごとに整数の内容をインクリメントします。このプログラムを実行してから のようなものを実行するab -n 10000 -c 1000 http://localhost:3000/と、同じ値が複数のヒットによってファイルから読み取られ、複数回書き込まれることが示されます。

注: flock() とfs-extについては知っています。ただし、flock() はファイルを現在のプロセスにロックします。ここでのすべてのアクセスは同じプロセス内にあるため、flock() は機能しません (そして、この例はかなり複雑になります)。

また、通常はエクスプレス、非同期などを使用して、このほとんどを実行することに注意してください。例のために基本に固執するだけです。

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

自分のやりたいことを実行するライブラリが見つからなかったので、ここで作成しました。

https://npmjs.org/package/schlock

これは、読み取り/書き込みロックを使用した上記のサンプルプログラムです。また、全体を読みやすくするために「ステップ」を使用しました。

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 に答える
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

データをファイルに保存することは、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 に答える
1

私が今見つけた解決策の1つnpm searchは、ロッカーサーバーです:https ://github.com/bobrik/locker 。

それは問題の良い解決策だと思います、そしてそれは私がそれをどのように設計するかについてです。

大きな問題は、別のサーバーを必要とする一般的なケース(リソースを使用する複数のプロセス)を処理することです。

私はまだほぼ同じことをするインプロセスソリューションを探しています。

于 2012-11-03T14:52:39.213 に答える
1

fs.writeFileSyncと readFileSyncを使用できます。

于 2012-11-03T04:16:17.793 に答える