Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/364.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/39.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Node.js fs.writeFile()清空文件_Javascript_Node.js_Fs - Fatal编程技术网

Javascript Node.js fs.writeFile()清空文件

Javascript Node.js fs.writeFile()清空文件,javascript,node.js,fs,Javascript,Node.js,Fs,我有一个更新方法,大约每16-40ms调用一次,里面有以下代码: this.fs.writeFile("./data.json", JSON.stringify({ totalPlayersOnline: this.totalPlayersOnline, previousDay: this.previousDay, gamesToday: this.gamesToday }), function (err) { if (err) { return

我有一个更新方法,大约每16-40ms调用一次,里面有以下代码:

this.fs.writeFile("./data.json", JSON.stringify({
    totalPlayersOnline: this.totalPlayersOnline,
    previousDay: this.previousDay,
    gamesToday: this.gamesToday
}), function (err) {
    if (err) {
        return console.log(err);
    }
});

如果服务器抛出错误,“data.json”文件有时会变为空。如何防止这种情况发生?

如果错误是由于输入错误(您要写入的数据)造成的,请确保数据正确无误,然后执行写入文件。 如果错误是由于writeFile失败引起的,即使输入正常,也可以检查函数是否已执行,直到文件写入为止。一种方法是使用async doWhilst函数

async.doWhilst(
    writeFile(), //your function here but instead of err when fail callback success to loop again
    check_if_file_null, //a function that checks that the file is not null
    function (err) {
        //here the file is not null
    }
);
问题 这不是一个原子操作。下面是一个示例程序,我将在其上运行
strace

#!/usr/bin/env node
const { writeFile, } = require('fs');

// nodejs won’t exit until the Promise completes.
new Promise(function (resolve, reject) {
    writeFile('file.txt', 'content\n', function (err) {
        if (err) {
            reject(err);
        } else {
            resolve();
        }
    });
});
当我在
strace-f
下运行它并整理输出以仅显示来自
writeFile
操作()的系统调用时,我得到:

如您所见,
writeFile
分三步完成

  • 该文件已被删除。这是一个原子操作,使用提供的标志在磁盘上创建一个空文件,或者,如果该文件存在,则将其截断。截断文件是确保文件中只包含您编写的内容的一种简单方法。如果文件中存在现有数据,并且该文件比随后写入该文件的数据长,则多余的数据将保留。要避免这种情况,请截断
  • 内容是书面的。因为我写了这么短的字符串,所以这是通过一个调用完成的,但是对于大量的数据,我假设nodejs可能一次只写一个块
  • 把手是关着的
  • My
    strace
    的每个步骤都发生在不同的节点IO线程上。这对我来说意味着,
    fs.writeFile()
    实际上可能在和方面实现。因此,nodejs不会将这个复杂的操作视为任何级别的原子操作,因为它不是原子操作。因此,如果您的节点进程在不等待操作完成的情况下终止,即使是正常终止,操作也可以在上述任何步骤中完成。在您的情况下,您会看到流程在
    writeFile()完成步骤1之后,但在完成步骤2之前退出

    解决方案 用POSIX层以事务方式替换文件内容的常见模式是使用以下步骤:

  • 将数据写入另一个名称不同的文件,即文件(请参阅中的“何时应该fsync?”),然后
    close()
    it
  • (或者,在Windows上,)在要替换的文件上使用不同名称的文件
  • 使用此算法,无论程序何时终止,都会更新或不更新目标文件。而且,更好的是,日志(现代)文件系统将确保,只要在继续执行步骤2之前在步骤1中对文件执行
    fsync()
    ,这两个操作将按顺序进行。也就是说,如果您的程序先执行步骤1,然后执行步骤2,但拔下插头,则在启动时,您会发现文件系统处于以下状态之一:

    • 这两个步骤都没有完成。原始文件是完整的(或者如果它以前从未存在过,那么它就不存在)。替换文件要么不存在(执行
      writeFile()
      算法的步骤1,
      open()
      ,实际上从未成功),要么存在但为空(执行
      writeFile()
      算法完成的步骤1),要么存在一些数据(执行
      writeFile()
      算法部分完成的步骤2)
    • 第一步完成了。原始文件是完整的(或者如果它以前不存在,现在仍然不存在)。替换文件已存在,包含所需的所有数据
    • 这两个步骤都已完成。在原始文件的路径上,您现在可以访问所有替换数据,而不是空白文件。在第一步中写入替换数据的路径不再存在
    使用此模式的代码可能如下所示:

    const { writeFile, rename, } = require('fs');
    
    function writeFileTransactional (path, content, cb) {
        // The replacement file must be in the same directory as the
        // destination because rename() does not work across device
        // boundaries.
    
        // This simple choice of replacement filename means that this
        // function must never be called concurrently with itself for the
        // same path value. Also, properly guarding against other
        // processes trying to use the same temporary path would make this
        // function more complicated. If that is a concern, a proper
        // temporary file strategy should be used. However, this
        // implementation ensures that any files left behind during an 
        //unclean termination will be cleaned up on a future run.
        let temporaryPath = `${path}.new`;
        writeFile(temporaryPath, content, function (err) {
            if (err) {
                return cb(err);
            }
    
            rename(temporaryPath, path, cb);
        });
    };
    

    这与在任何语言/框架中解决同一问题所使用的解决方案基本相同。

    我没有用它运行一些真正的测试,我只是在手动重新加载ide时注意到,有时文件是空的。 我首先尝试的是重命名方法,并注意到了相同的问题,但重新创建一个新文件不太理想(考虑到文件监视等)

    我的建议或我现在正在做的是在您自己的readFileSync中,我检查文件是否丢失或返回的数据是否为空,然后休眠100毫秒,然后再试一次。我认为一个第三次尝试会有更大的延迟,这真的会把西格玛推到一个缺口,但目前不会这么做,因为增加的延迟可能是不必要的负面影响(在这一点上会考虑一个承诺)。还有其他与您自己的代码相关的恢复选项机会,您可以添加这些选项,以防万一。文件未找到或为空?基本上是另一种重试方式

    我的自定义writeFileSync添加了一个标志,可以在使用重命名方法(使用write sub dir.\u new“创建)或普通直接方法之间切换,因为代码的需要可能会有所不同。我的建议是根据文件大小来确定

    在这个用例中,文件很小,一次只能由一个节点实例/服务器更新。我可以看到添加随机文件名作为另一个带有重命名的选项,以允许多台机器在需要时为以后编写另一个选项。也可能是重试限制参数

    我还认为,您可以写入本地临时文件,然后通过某种方式复制到共享目标文件(也可以在目标文件上重命名以提高速度),当然,还可以进行清理(取消与本地临时文件的链接)。我猜这个想法有点像是把它推到shell命令上,所以不是更好。
    无论如何,这里的主要思想仍然是,如果发现是空的,就读两遍。我确信它不会被部分写入,通过nodejs8+加载到一个共享的Ubuntu类型的NFS挂载上,对吗

    一,。使用临时名称2写入新文件。将旧文件重命名为3。将新文件重命名为目标名称4。删除旧文件每次写入两次,第一次为
    const { writeFile, rename, } = require('fs');
    
    function writeFileTransactional (path, content, cb) {
        // The replacement file must be in the same directory as the
        // destination because rename() does not work across device
        // boundaries.
    
        // This simple choice of replacement filename means that this
        // function must never be called concurrently with itself for the
        // same path value. Also, properly guarding against other
        // processes trying to use the same temporary path would make this
        // function more complicated. If that is a concern, a proper
        // temporary file strategy should be used. However, this
        // implementation ensures that any files left behind during an 
        //unclean termination will be cleaned up on a future run.
        let temporaryPath = `${path}.new`;
        writeFile(temporaryPath, content, function (err) {
            if (err) {
                return cb(err);
            }
    
            rename(temporaryPath, path, cb);
        });
    };