我有两个节点线程正在运行,一个正在查看目录以供文件使用,另一个负责将文件写入给定目录。
通常它们不会在同一目录上运行,但对于我正在研究的边缘情况,它们将是。
消费应用似乎在文件完全写入之前抓取文件,导致文件损坏。
有没有办法可以锁定文件直到写完成?我查看了lockfile
模块,但不幸的是我不相信它适用于这个特定的应用程序。
=====
完整的代码放在这里是有意义的,但它的要点是:
监听器:
fs.writeFile
观察:
chokidar
跟踪每个监视目录中添加的文件fs.access
时,确保我们可以访问该文件
fs.access
似乎没有被文件写入fs.createReadStream
使用,然后发送到服务器
在这种情况下,文件将导出到监视目录,然后重新导入 通过手表过程。
答案 0 :(得分:3)
为此,我将使用proper-lockfile。您可以指定重试次数或使用重试配置对象来使用指数退避策略。这样,您可以处理两个进程需要同时修改同一文件的情况。
这是一个简单的示例,其中包含一些重试选项:
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();
});
答案 1 :(得分:0)
编写锁状态系统实际上非常简单。我找不到我这样做的地方,但想法是:
锁定文件只是一个目录中的空文件。每个锁定文件的名称都来自它所代表的文件的完整路径的哈希值。我使用的是MD5(速度相对较慢),但只要您确信路径字符串不会发生冲突,任何哈希算法都应该没问题。
这不是100%线程安全的,因为(除非我错过了一些愚蠢的东西)你不能自动检查文件是否存在并在Node中创建它,但在我的用例中,我拿着锁10秒或更长时间,因此微秒的竞争条件似乎没有那么大的威胁。如果您在同一文件上每秒持有并释放数千个锁,则此竞争条件可能适用于您。
这些只是咨询锁,显然,因此您需要确保代码请求锁定并捕获预期的异常。