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.watch
API, предоставляемые каждой платформой, используются соответственно, поэтому единообразие правил реализации этих 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 для отслеживания изменений папок легко, но трудно обеспечить точные обратные вызовы.Есть две основные трудности:
- Сглаживание различий между операционными системами, что требует объединения
fs.watch
При этом добавляются некоторые дополнительные механизмы проверки и механизмы задержки. - Необходимо различать ожидания операционной системы и ожидания пользователей, например, следует игнорировать дополнительные операции редактора и многократные операции чтения и записи операционной системы.
Существуют и другие факторы, такие как совместимость, разрешения, мягкие соединения и т. Д.fs.watch
API инженерного уровня не доступен из коробки.
еще 5 обсуждений
Адрес обсуждения:Интенсивное чтение «Как использовать Nodejs для мониторинга папок» · Выпуск № 87 · dt-fe/weekly
Если вы хотите принять участие в обсуждении, пожалуйста,кликните сюда, с новыми темами каждую неделю, выходящими по выходным или понедельникам.