- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个大约每 16-40 毫秒调用一次的更新方法,里面有这段代码:
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”文件有时会变成空的。我该如何防止这种情况发生?
最佳答案
fs.writeFile
不是原子操作。这是一个示例程序,我将在其上运行 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
操作 (which spans multiple IO threads, actually) 的系统调用时,我得到:
open("file.txt", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 9
pwrite(9, "content\n", 8, 0) = 8
close(9) = 0
如您所见,writeFile
分三步完成。
open()
编辑。这是一个原子操作,它使用提供的标志在磁盘上创建一个空文件,或者如果文件存在,则将其 chop 。 chop 文件是确保只有您写入的内容最终出现在文件中的一种简单方法。如果文件中存在现有数据并且文件比您随后写入文件的数据长,则额外的数据将保留。为避免这种情况,您会 chop 。pwrite()
完成的。调用,但对于大量数据,我认为 nodejs 可能一次只写入一个 block 。我的 strace
让这些步骤中的每一个都发生在不同的 Node IO 线程上。这向我暗示 fs.writeFile()
可能实际上是根据 fs.open()
实现的, fs.write()
, 和 fs.close()
.因此,nodejs 不会将此复杂操作视为任何级别的原子操作——因为它不是。因此,如果您的 Node 进程在没有等待操作完成的情况下终止,即使是正常终止,操作也可以在上述任何步骤中进行。在您的情况下,您会看到您的进程在 writeFile()
完成第 1 步之后但在完成第 2 步之前退出。
用 POSIX 层事务性替换文件内容的常见模式是使用以下步骤:
fsync()
文件(参见 “Ensuring data reaches disk” 中的“你什么时候应该 fsync?”),然后 close()
它。rename()
(或者,在 Windows 上,MoveFileEx()
with MOVEFILE_REPLACE_EXISTING
)您要替换的文件上的不同名称的文件。使用此算法,无论您的程序何时终止,目标文件都会更新或不更新。而且,更好的是,日志式(现代)文件系统将确保,只要您在步骤 1 中 fsync()
文件,然后再继续执行步骤 2,这两个操作就会按顺序进行。即,如果您的程序执行第 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);
});
};
这基本上是您在任何语言/框架中用于解决相同问题的相同解决方案。
关于javascript - Node.js fs.writeFile() 清空文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30886217/
我是一名优秀的程序员,十分优秀!