Изящно завершить работу сервера сокетов UNIX на NodeJS, работающем под Forever

18

У меня есть приложение NodeJS, которое настраивает UNIX-сокет для предоставления некоторого межпроцессного канала связи (некоторый вид мониторинга). Файл сокета UNIX помещается в папку os.tmpdir() (т.е. /tmp/app-monitor.sock ).

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

Мое приложение работает с forever start ... для мониторинга его жизненного цикла.

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

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

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

Итак, вопрос в том ... Как лучше справиться с такой ситуацией (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 ответов

40

Как уже упоминали другие, вы ничего не можете сделать в ответ на 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, чтобы делать то, что вы хотите: process.on('SIGTERM', shutdown)

    
ответ дан 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, тогда вы должны очистить руководство по сокету

этот пример отлично работает с forever

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 и хотите использовать вечно (который использует 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 на другой сигнал в forever-monitor для обработки в приложении
  2. позаботьтесь о своем приложении в этом случае, используя fs.unlink
  3. прекратить использовать навсегда
ответ дан amirka 09.05.2013 в 15:56