Изящно завершение работы сервера UNIX-сокетов в NodeJS под управлением Forever

17

У меня есть приложение NodeJS, которое устанавливает UNIX-сокет, чтобы открыть какой-то канал межпроцессного обмена (какой-то материал для мониторинга). Файл UNIX-сокета помещается в папку os.tmpdir() (т. Е.% Co_de%).

var net = require('net');
var server = net.createServer(...);
server.listen('/tmp/app-monitor.sock', ...);

Я использую обработку сигналов (SIGINT, SITERM и т. д.), чтобы изящно завершить работу моего сервера и удалить файл сокета.

function shutdown() {
    server.close(); // socket file is automatically removed here
    process.exit();
}

process.on('SIGINT', shutdown);
// and so on

Мое приложение работает с /tmp/app-monitor.sock , чтобы контролировать его жизненный цикл.

У меня проблема с командой forever start ... . Когда навсегда выполняется forever restartall , используется restartall для завершения всех дочерних процессов. SIGKILL не может обрабатываться процессом, поэтому мое приложение умирает без каких-либо остановок.

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

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

Итак, вопрос в том, что ... Каков лучший способ справиться с такой ситуацией (сервер SIGKILL и UNIX-сокетов)?

    
задан Olegas 23.04.2013 в 22:08
источник
  • прочитал этот файл nodejs.org/api/all.html#all_signal_events –  wayne 24.04.2013 в 03:38
  • Да, и вы прочитали мой вопрос? –  Olegas 24.04.2013 в 06:36
  • нет Я не читал. Это легко сказать, чем делать. Если вы не возражаете изменить код навсегда, то навсегда / node_modules / forever-monitor / lib / forever-monitor / monitor.js, в функции Monitor.prototype.kill добавьте SIGINT, прежде чем навсегда отправить сигнал SIGKILL –  wayne 24.04.2013 в 07:54
  • @wayne Это плохое решение. Я не хочу изменять стороннее программное обеспечение. Кроме того, мой процесс может быть убит с SIGKILL без навсегда ... –  Olegas 24.04.2013 в 09:19
  • FYI node-dev отправляет SIGTERM, который является захватывающим. –  mpen 18.04.2017 в 23:20

5 ответов

38

Как уже отмечали другие люди, вы ничего не можете сделать в ответ на SIGKILL, что, в общем, почему forever (и все остальные) не должны использовать SIGKILL, за исключением чрезвычайных обстоятельств. Поэтому лучшее, что вы можете сделать, это очистить в другом процессе.

Я предлагаю вам очистить при запуске. Когда вы получите EADDRINUSE , попробуйте подключиться к сокету. Если соединение сокета завершается успешно, выполняется другой сервер, и этот экземпляр должен выйти. Если соединение не удастся, безопасно отсоединить файл сокета и создать новый.

var fs = require('fs');
var net = require('net');
var server = net.createServer(function(c) { //'connection' listener
    console.log('server connected');
    c.on('end', function() {
        console.log('server disconnected');
    });
    c.write('hello\r\n');
    c.pipe(c);
});

server.on('error', function (e) {
    if (e.code == 'EADDRINUSE') {
        var clientSocket = new net.Socket();
        clientSocket.on('error', function(e) { // handle error trying to talk to server
            if (e.code == 'ECONNREFUSED') {  // No other server listening
                fs.unlinkSync('/tmp/app-monitor.sock');
                server.listen('/tmp/app-monitor.sock', function() { //'listening' listener
                    console.log('server recovered');
                });
            }
        });
        clientSocket.connect({path: '/tmp/app-monitor.sock'}, function() { 
            console.log('Server running, giving up...');
            process.exit();
        });
    }
});

server.listen('/tmp/app-monitor.sock', function() { //'listening' listener
    console.log('server bound');
});
    
ответ дан Old Pro 12.05.2013 в 01:26
источник
  • Отличный ответ. Только одна «ошибка»: когда вы вызываете fs.unlink, вы должны передать обратный вызов и снова перезапустить server.listen на этот обратный вызов. Таким образом, вы никогда не пытаетесь подключиться перед отключением (также узел предупреждает fs: отсутствует обратный вызов, когда не используется обратный вызов). –  Salvatorelab 26.03.2014 в 11:42
  • @ TheBronx, пожалуйста, отредактируйте ответ, чтобы включить ваши рекомендации / улучшения. –  Old Pro 28.03.2014 в 08:18
  • Немного не по теме: Мне кажется, что для этого было разрешено редактирование пробелов с 2 пробелов до 4-х мест. –  Claudiu 28.03.2014 в 09:26
  • @Claudius: Так верно, я действительно пытался отклонить его, но было слишком поздно. –  Stijn de Witt 28.03.2014 в 09:27
  • @TheBronx. Поскольку это проблема запуска сервера с практически неизбежным условием гонки, я хочу использовать синхронную функцию отключения связи, чтобы минимизировать время между удалением сокета и созданием нового. Я считаю, что fs.unlink был синхронным, когда я писал это (я, конечно, не получил предупреждения, когда я его запускал), но теперь, когда есть явная синхронная версия, я заменил ваше изменение на это. –  Old Pro 30.03.2014 в 04:56
Показать остальные комментарии
2

вы можете использовать SIGTERM для выполнения того, что вы хотите: % Co_de%     

ответ дан Clintm 07.05.2013 в 23:26
источник
  • Я могу обрабатывать SIGTERM, но навсегда перезапускает дочерние процессы SIGKILL –  Olegas 08.05.2013 в 11:28
  • @Olegas - навсегда есть возможность изменить SIG, используемый, когда он останавливает процесс. Вы можете изменить его на SIGTERM. –  jfriend00 16.09.2014 в 09:21
1
server.on('error', function (e) {
  if (e.code == 'EADDRINUSE') {
    console.log('Address in use, retrying...');
    setTimeout(function () {
      server.close();
      server.listen(PORT, HOST);
    }, 1000);
  }
});

Ссылка

UPD

вы не можете обрабатывать SIGKILL, тогда вы должны очистить сокет ручной

этот пример отлично работает навсегда

var fs = require('fs');
var net = require('net');
var server = net.createServer(function(c) {});
server.listen('./app-monitor.sock', function() {
  console.log('server bound');
});

server.on('error', function (e) {
  if (e.code == 'EADDRINUSE') {
    console.log('Address in use, retrying...');
    setTimeout(function () {
      fs.unlink('./app-monitor.sock');
    }, 1000);
  }
});
    
ответ дан amirka 08.05.2013 в 01:32
источник
  • Но EADDRINUSE в случае сервера файлового сокета означает, что файл сокета существует. Он никогда не будет закрываться или исчезать, потому что «родительское приложение уже мертво, убито SIGKILL –  Olegas 08.05.2013 в 11:31
  • SIGKILL закрыть программу немедленно использовать fs.unlink вместо server.close () –  amirka 08.05.2013 в 15:03
  • ... и я могу потенциально убить сокет из другого запущенного экземпляра. –  Olegas 08.05.2013 в 20:10
  • Сокеты UNIX являются объектами файловой системы, вам нужно очистить их после завершения. Перед тем, как выйти, обязательно вызовите server.close (). если вы не можете этого сделать, вы должны исправить это навсегда - монитор, чтобы отправить другой сигнал или в ваше приложение через fs.unlink –  amirka 09.05.2013 в 15:51
1

Поскольку вы не можете обработать SIGKILL и хотите использовать forever (который использует SIGKILL ), вам придется использовать обходной путь.

Например, сначала отправьте сигнал для выключения вашего сервера, а затем выполните перезапуск навсегда:

kill -s SIGUSR1 $pid
# $pid contains the pid of your node process, or use
# killall -SIGUSR1 node

sleep 2
forever restart test.js

В дескрипторе js SIGUSR1:

process.on('SIGUSR1', gracefullyShutdownMyServer);
    
ответ дан laktak 08.05.2013 в 21:56
источник
0

вы должны выбрать

  1. измените SIGKILL на другой сигнал навсегда-мониторе, чтобы обрабатывать приложение
  2. позаботьтесь о своем приложении в этом случае, используя fs.unlink
  3. остановить использование навсегда
ответ дан amirka 09.05.2013 в 15:56
источник