我有两个节点线程在运行,一个观察目录以使用文件,另一个负责将文件写入给定目录。
通常,它们不会在同一目录上运行,但对于我正在处理的边缘情况,它们将是。
似乎消费应用程序在文件被完全写入之前抓取文件,导致文件损坏。
有没有办法锁定文件直到写入完成?我已经研究过这个lockfile模块,但不幸的是我不相信它适用于这个特定的应用程序。
======
把完整的代码放在这里远远没有意义,但它的要点是:
听众:
fs.writeFile观察者:
chokidar跟踪每个被监视目录中添加的文件fs.access被调用以确保我们可以访问该文件
fs.access 似乎对正在写入的文件无动于衷fs.createReadStream然后发送到服务器
在这种情况下,文件被导出到被监视的目录,然后被监视进程重新导入。
DJD*_*ark 11
我会为此使用正确的锁文件。您可以指定重试次数或使用重试配置对象来使用指数退避策略。这样您就可以处理两个进程需要同时修改同一个文件的情况。
这是一个带有一些重试选项的简单示例:
const lockfile = require('proper-lockfile');
const Promise = require('bluebird');
const fs = require('fs-extra');
const crypto = require('crypto'); // random buffer contents
const retryOptions = {
retries: {
retries: 5,
factor: 3,
minTimeout: 1 * 1000,
maxTimeout: 60 * 1000,
randomize: true,
}
};
let file;
let cleanup;
Promise.try(() => {
file = '/var/tmp/file.txt';
return fs.ensureFile(file); // fs-extra creates file if needed
}).then(() => {
return lockfile.lock(file, retryOptions);
}).then(release => {
cleanup = release;
let buffer = crypto.randomBytes(4);
let stream = fs.createWriteStream(file, {flags: 'a', encoding: 'binary'});
stream.write(buffer);
stream.end();
return new Promise(function (resolve, reject) {
stream.on('finish', () => resolve());
stream.on('error', (err) => reject(err));
});
}).then(() => {
console.log('Finished!');
}).catch((err) => {
console.error(err);
}).finally(() => {
cleanup && cleanup();
});
Run Code Online (Sandbox Code Playgroud)
编写锁状态系统实际上非常简单。我找不到我在哪里做的,但我的想法是:
锁定文件只是单个目录中的空文件。每个锁定文件从它所代表的文件的完整路径的散列中获取其名称。我使用了 MD5(相对较慢),但是只要您确信路径字符串不会发生冲突,任何哈希算法都应该没问题。
这不是 100% 线程安全的,因为(除非我错过了一些愚蠢的事情)你无法自动检查文件是否存在并在 Node 中创建它,但在我的用例中,我持有锁 10 秒或更重要的是,微秒竞争条件似乎并没有那么大的威胁。如果您每秒在同一文件上持有和释放数千个锁,那么这种竞争条件可能适用于您。
显然,这些只是咨询锁,因此您需要确保您的代码请求锁定并捕获预期的异常。