Статьи

Перезапуск Node.js при изменении источника

Мне лень. Я помню, как читал где-то, что это была желательная черта для разработчика. Хотя я не уверен, где, и, честно говоря, это слишком много усилий, чтобы связать это. Эта лень недавно вышла на первый план, когда я играл с Node.

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

Поэтому мне было интересно, есть ли в Node что-то встроенное для отслеживания изменений в файле. Я не увидел ничего полезного в файле node.exe — помощь, и исследование его в Интернете просто так утомительно, поэтому я решил написать собственное решение.

В поисках некоторых изменений

В .NET есть класс System.IO.FileSystemWatcher . С экземпляром этого класса вы можете отслеживать файлы в каталоге на предмет изменений. Я настроил это так:

var watcher = new FileSystemWatcher(); 
watcher.Path = @"C:\node.js\stuff"; 
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName; 
watcher.Filter = "*.js"; 

watcher.Changed += Changed; 
watcher.Created += Changed; 
watcher.Deleted += Changed; 
watcher.Renamed += Renamed; 

// Begin watching 
watcher.EnableRaisingEvents = true;

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

Далее, существует ряд событий, которые необходимо передать, чтобы ответить на изменения. Я использовал один и тот же обработчик столько, сколько мог, потому что я всегда хочу делать одно и то же: перезапустить Node. Также не совсем очевидно, как эти события связаны с NotifyFilter, но я не слишком углубился в это. (Я ленивый помню.)

Также важно установить EnableRaisingEvents. Если вы этого не сделаете, то (угадайте, что) событие не возникает.

Убить, убить, убить

Теперь, когда происходит значительное изменение, настало время перезапустить узел. Для этого я использовал System.Diagnostics.Process . Это немного сложно, с несколькими неочевидными ручками для поворота.

Во-первых, мне нужно получить ссылку на процесс Node. Я заметил в диспетчере задач, что имя процесса было «узел». Так что я использовал

Process.GetProcessesByName("node")

Это возвращает массив процессов, и поэтому я сделал это:

var matches = Process.GetProcessesByName("node");

matches.ToList().ForEach(match => {
    Console.WriteLine("attempting to close node.js [" + match.Id + "]");
    match.Kill();
    match.WaitForExit(300); // it shouldn’t take this long, we’re just being cautious
    Console.WriteLine("successfully closed");

По общему признанию, это поражает это молотком. Это нормально, потому что для меня это просто
быстрый и
грязный вспомогательный инструмент, а не производственное приложение.

После завершения процесса я хочу начать еще один. Теперь мне не нужно, чтобы при каждом перезапуске появлялось другое окно консоли, вместо этого я просто хотел бы перенаправить ввод и вывод в мое маленькое вспомогательное приложение. Это может быть немного сложнее, и мне пришлось немного поэкспериментировать, чтобы найти правильную комбинацию, чтобы не мешать. Если вы обнаружите, что это не так, я рекомендую поискать в StackOverflow . Я нашел несколько полезных вопросов там. Одна из клавиш, которая появлялась более одного раза, — захват и закрытие потока для стандартного ввода.

var start = new ProcessStartInfo();
start.FileName = @"C:\node.js\node.exe";
start.UseShellExecute = false; // the starts the process directly, as opposed to going thu the shell
start.CreateNoWindow = true; // we don’t want a new window
start.RedirectStandardOutput = true;
start.RedirectStandardInput = true;
start.Arguments = Path.Combine(@"C:\node.js\stuff", @"server.js");

var node = new Process();
node.EnableRaisingEvents = true;
node.OutputDataReceived += OutputHandler;
node.StartInfo = start;
node.Start();

node.Refresh(); // refreshing the metadata stored in the instance of Process 
Console.WriteLine(node.ProcessName);
Console.WriteLine("[" + node.Id + "] node.exe started");

// close the input, we won't use it
var input = node.StandardInput;
input.Close();

// and now for the output
node.BeginOutputReadLine();

Сначала мы создаем объект, который содержит конфигурацию для запуска экземпляра Node. Обратите внимание, что мы передаем файл server.js в качестве аргумента.

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

Наконец, я обработал перенаправление ввода и вывода.

Полный код приложения доступен по адресу https://gist.github.com/1108727 .

эпилог

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