如何在异步写入文件时锁定文件

时间:2016-02-25 00:58:57

标签: node.js fs

我有两个节点线程正在运行,一个正在查看目录以供文件使用,另一个负责将文件写入给定目录。

通常它们不会在同一目录上运行,但对于我正在研究的边缘情况,它们将是。

消费应用似乎在文件完全写入之前抓取文件,导致文件损坏。

有没有办法可以锁定文件直到写完成?我查看了lockfile模块,但不幸的是我不相信它适用于这个特定的应用程序。

=====

完整的代码放在这里是有意义的,但它的要点是:

  1. App旋转观察者和听众
  2. 监听器:

    • 侦听正在添加到db的文件,使用fs.writeFile
    • 将其导出

    观察:

    • 观察者使用chokidar跟踪每个监视目录中添加的文件
    • 发现fs.access时,确保我们可以访问该文件
      • fs.access似乎没有被文件写入
    • 文件通过fs.createReadStream使用,然后发送到服务器
      • 文件流是必要的,因为我们需要文件哈希

    在这种情况下,文件将导出到监视目录,然后重新导入 通过手表过程。

2 个答案:

答案 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)

编写锁状态系统实际上非常简单。我找不到我这样做的地方,但想法是:

  1. 获取锁定时创建锁定文件,
  2. 释放锁时删除它们,
  3. 发生超时后删除它们,
  4. 只要请求锁定已存在锁定文件的文件,就会
  5. 抛出。
  6. 锁定文件只是一个目录中的空文件。每个锁定文件的名称都来自它所代表的文件的完整路径的哈希值。我使用的是MD5(速度相对较慢),但只要您确信路径字符串不会发生冲突,任何哈希算法都应该没问题。

    这不是100%线程安全的,因为(除非我错过了一些愚蠢的东西)你不能自动检查文件是否存在并在Node中创建它,但在我的用例中,我拿着锁10秒或更长时间,因此微秒的竞争条件似乎没有那么大的威胁。如果您在同一文件上每秒持有并释放数千个锁,则此竞争条件可能适用于您。

    这些只是咨询锁,显然,因此您需要确保代码请求锁定并捕获预期的异常。