Интенсивное чтение "Как использовать Nodejs для мониторинга папок"

Node.js внешний интерфейс Операционная система NPM

1. Введение

Статьи, прочитанные в этом выпуске:How to Watch for Files Changes in Node.js, обсуждает, как отслеживать изменения файлов.

Если вы хотите использовать существующую библиотеку, рекомендуетсяchokidarилиnode-watch, если хотите понять принцип реализации, читайте ниже.

2 Обзор

Используйте файл fs.watch

использоватьfsвстроенная функцияwatchfileкажется, делает трюк:

fs.watchFile(dir, (curr, prev) => {});

Но вы можете обнаружить, что в выполнении этого обратного вызова есть определенная задержка, потому чтоwatchfileОн обнаруживает изменения в файлах посредством опроса, не может давать обратную связь в режиме реального времени и может отслеживать только один файл, который имеет проблемы с эффективностью.

Используйте fs.watch

использоватьfsЕще одна встроенная функцияwatchэто лучший вариант:

fs.watch(dir, (event, filename) => {});

watchЧерез механизм уведомления об изменении файла, предоставляемый операционной системой, inotify используется в операционной системе Linux, FSEvents используется в системе macOS, а ReadDirectoryChangesW используется в системе Windows и может использоваться для отслеживания изменений каталога.fs.watchfileГораздо эффективнее.

$ node file-watcher.js
[2018-05-21T00:55:52.588Z] Watching for file changes on ./button-presses.log
[2018-05-21T00:56:00.773Z] button-presses.log file Changed
[2018-05-21T00:56:00.793Z] button-presses.log file Changed
[2018-05-21T00:56:00.802Z] button-presses.log file Changed
[2018-05-21T00:56:00.813Z] button-presses.log file Changed

Но когда мы модифицируем файл, обратный вызов выполняется 4 раза! Причина в том, что при записи файла может быть инициировано несколько операций записи, даже если он сохраняется только один раз. Но нам такой чувствительный callback не нужен, потому что обычно считается, что сохранение — это модификация, и нам все равно, сколько раз система записывала файл в низ.

Следовательно, можно дополнительно судить о том, является ли состояние триггераchange:

fs.watch(dir, (event, filename) => {
  if (filename && event === "change") {
    console.log(`${filename} file Changed`);
  }
});

Это может решить проблему в определенной степени, но автор обнаружил, что система Raspbian не поддерживаетrenameсобытие, если оно классифицируется какchange, делая такое суждение бессмысленным.

Что автор хочет выразить, так это то, что на разных платформахfs.watchПравила могут отличаться, потому чтоfs.watchAPI, предоставляемые каждой платформой, используются соответственно, поэтому единообразие правил реализации этих API не может быть гарантировано.

Схема оптимизации 1: Сравните время модификации файла

на основеfs.watch, добавляя суждение о времени модификации:

let previousMTime = new Date(0);

fs.watch(dir, (event, filename) => {
  if (filename) {
    const stats = fs.statSync(filename);
    if (stats.mtime.valueOf() === previousMTime.valueOf()) {
      return;
    }
    previousMTime = stats.mtime;
    console.log(`${filename} file Changed`);
  }
});

Лог изменился с 4 на 3, но проблемы все равно есть. Мы считаем, что изменения в содержимом файла считаются модификациями, но операционная система учитывает больше факторов, поэтому попробуем сравнить, изменилось ли содержимое файла.

Автор добавляет: Некоторые другие редакторы с открытым исходным кодом могут очистить файл перед записью, что также повлияет на количество срабатываний обратного вызова.

Схема оптимизации 2: проверка файла md5

Только когда содержимое файла изменяется, считается, что изменение было инициировано.Это всегда нормально:

let md5Previous = null;

fs.watch(dir, (event, filename) => {
  if (filename) {
    const md5Current = md5(fs.readFileSync(buttonPressesLogFile));
    if (md5Current === md5Previous) {
      return;
    }
    md5Previous = md5Current;
    console.log(`${filename} file Changed`);
  }
});

Лог наконец-то поменялся с 3 на 2, зачем еще один? Возможная причина в том, что в процессе сохранения файла система может инициировать несколько событий обратного вызова, а также может быть промежуточное состояние.

Третий план оптимизации: добавление механизма задержки

Мы пытаемся задержать суждение на 100 миллисекунд, возможно, мы сможем избежать промежуточного состояния:

let fsWait = false;
fs.watch(dir, (event, filename) => {
  if (filename) {
    if (fsWait) return;
    fsWait = setTimeout(() => {
      fsWait = false;
    }, 100);
    console.log(`${filename} file Changed`);
  }
});

Теперь журнал становится одним. Многие пакеты npm используют функцию устранения дребезга для управления частотой срабатывания здесь, чтобы корректировать частоту срабатывания.

И нам нужно объединить md5 с механизмом задержки, чтобы получить относительно точные результаты:

let md5Previous = null;
let fsWait = false;
fs.watch(dir, (event, filename) => {
  if (filename) {
    if (fsWait) return;
    fsWait = setTimeout(() => {
      fsWait = false;
    }, 100);
    const md5Current = md5(fs.readFileSync(dir));
    if (md5Current === md5Previous) {
      return;
    }
    md5Previous = md5Current;
    console.log(`${filename} file Changed`);
  }
});

3 Интенсивное чтение

Автор обсуждает некоторые основные способы реализации мониторинга папок, видно, что используется собственный API каждой платформы.fs.watchНе так надежно, но это единственный способ, которым мы можем отслеживать файлы, поэтому нам нужно сделать ряд оптимизаций на его основе.

В реальных сценариях также необходимо учитывать различие между папками и файлами, программными подключениями и разрешениями на чтение и запись.

Кроме того, библиотека, используемая в производственной среде, в основном использует от 50 до 100 миллисекунд для решения проблемы повторного срабатывания.

Так что неважноchokidarилиnode-watch, все используют техники, упомянутые в статье, плюс обработка граничных условий, обработка мягких подключений, разрешений и т.д., и учитываются все возможные ситуации, чтобы обеспечить более точный обратный вызов.

Например, чтобы определить, завершена ли операция записи файла, также необходимо опросить:

function awaitWriteFinish() {
  // ...省略
  fs.stat(
    fullPath,
    function(err, curStat) {
      // ...省略

      if (prevStat && curStat.size != prevStat.size) {
        this._pendingWrites[path].lastChange = now;
      }

      if (now - this._pendingWrites[path].lastChange >= threshold) {
        delete this._pendingWrites[path];
        awfEmit(null, curStat);
      } else {
        timeoutHandler = setTimeout(
          awaitWriteFinish.bind(this, curStat),
          this.options.awaitWriteFinish.pollInterval
        );
      }
    }.bind(this)
  );
  // ...省略
}

Видно, что сторонняя библиотека npm использует метод недоверия к обратному вызову операционной системы и полностью переписывает логику оценки в соответствии с информацией о файле.

Видно, что доверие к обратному вызову операционной системы не может сгладить различия между всеми операционными системами, только единообразно переписав логику «записи», «удаления» и «модификации» файлов, можно гарантировать совместимость между платформами.

4 Резюме

Использовать nodejs для отслеживания изменений папок легко, но трудно обеспечить точные обратные вызовы.Есть две основные трудности:

  1. Сглаживание различий между операционными системами, что требует объединенияfs.watchПри этом добавляются некоторые дополнительные механизмы проверки и механизмы задержки.
  2. Необходимо различать ожидания операционной системы и ожидания пользователей, например, следует игнорировать дополнительные операции редактора и многократные операции чтения и записи операционной системы.

Существуют и другие факторы, такие как совместимость, разрешения, мягкие соединения и т. Д.fs.watchAPI инженерного уровня не доступен из коробки.

еще 5 обсуждений

Адрес обсуждения:Интенсивное чтение «Как использовать Nodejs для мониторинга папок» · Выпуск № 87 · dt-fe/weekly

Если вы хотите принять участие в обсуждении, пожалуйста,кликните сюда, с новыми темами каждую неделю, выходящими по выходным или понедельникам.