计算何时多次写入文件会导致不准确?

时间:2016-02-15 20:16:04

标签: javascript node.js mongodb

在我的节点服务器中我有一个变量

var clicks = 0;

每次用户点击webapp时,websocket事件都会发送一条消息。在服务器上,

clicks++;
if (clicks % 10 == 0) {
  saveClicks();
}

function saveClicks() {
    var placementData = JSON.stringify({'clicks' : clicks});
    fs.writeFile( __dirname + '/clicks.json', placementData, function(err) {
    });
}

我必须以什么速度开始担心覆盖?我该如何计算这个数学?

(我正在为每次点击创建一个MongoDB json对象,但我很好奇本机解决方案可以提供什么)。

2 个答案:

答案 0 :(得分:4)

来自fs.writeFile()的node.js文档:

  

请注意,多次使用fs.writeFile()是不安全的   相同的文件,无需等待回调。对于这种情况,   强烈建议使用fs.createWriteStream()

这不是一个数学问题,无法确定何时可能导致问题 - 它只是一个错误的代码,在无法预测的情况下会给你一个冲突的机会。 node.js doc明确指出这可能会导致冲突。

为了确保您不会发生冲突,请以不同的方式编写代码,以免发生冲突。

如果您想确保所有写入按正确的传入请求顺序发生,那么最后一个到达请求始终是最终在文件中的那个,那么您需要在数据到达时对其进行排队(所以订单被保留)并以打开文件进行独占访问的方式写入文件,因此在先前请求仍在编写并正确处理争用错误时,没有其他请求可以写入。

这是数据库主要为您自动执行的问题,因此可能是使用数据库的一个原因。

假设您没有使用群集,因此没有多个进程尝试写入此文件,并且您只是想确保发送的最后一个值是通过此过程写入文件的值,您可以做这样的事情:

var saveClicks = (function() {
    var isWriting = false;
    var lastData;
    return function() {
        // always save most recent data here
        lastData = JSON.stringify({'clicks' : clicks});
        if (!isWriting) {
            writeData(lastData);
        }

        function writeData(data) {
            isWriting = true;
            lastData = null;
            fs.writeFile(__dirname + '/clicks.json', data, function(err) {
                isWriting = false;
                if (err) {
                    // decide what to do if an error occurs
                }
                // if more data arrived while we were writing this, then write it now
                if (lastData) {
                    writeData(lastData);
                }
            });
        }
    }
})();

答案 1 :(得分:1)

@jfriend00对于createWriteStream肯定是正确的,并且已经对数据库提出了一个观点,并且几乎所有内容都说了,但我想强调关于数据库的观点,因为基本上文件保存方法似乎很奇怪我

因此,请使用数据库。

这不仅会让你免于跟踪这些事情的麻烦,而且会显着加快速度(请记住,在节点中完成这些工作的方式,大量的文件读写过程将在一个线程中并行化,所以基本上如果其中一个持续多年,它可能会略微影响整体表现。)

Redis是存储键值数据的完美解决方案,因此您可以在Redis数据库中存储每个用户的点击数据,无论如何,当您获得足够的流量时,您必须与它们一起运行:)< / p>

如果您还不相信,请看一下这个简单的基准:

Redis的:

var async = require('async');
var redis = require("redis"),
    client = redis.createClient();

console.time("To Redis");
async.mapLimit(new Array(100000).fill(0), 1, (el, cb) => client.set("./test", 777, cb), () => {
    console.timeEnd("To Redis");
});
  

To Redis:5410.383ms

FS

var async = require('async');
var fs = require('fs');

console.time("To file");
async.mapLimit(new Array(100000).fill(0), 1, (el, cb) => fs.writeFile("./test", 777, cb), () => {
    console.timeEnd("To file");
});
  

提交:20344.749ms

顺便说一下,只需将这个“点击保护程序”添加到套接字socket.on('disconnect', ...,即可显着增加存储进度的点击次数(现在为10)。