Я работаю над реализацией изящного завершения работы для API узла. Как правило, во время разработки этот процесс запускается с использованием обычного сценария start
, но я заметил, что это вызывает некоторое раздражающее поведение, которое я удивляюсь, которого я никогда не замечал раньше за свои 3 года или около того как разработчик узла.
Чтобы начать отключение во время разработки, мы просто нажимаем ctrl + C в терминале bash, что, конечно, приводит к отправке SIGINT
процессу npm и всем его дочерним процессам. Я могу захватить это с помощью обработчика process.on
, чтобы инициировать постепенное отключение оттуда - закрытие новых запросов, ожидание существующих запросов до конца sh, затем уничтожение соединения с базой данных, все эти хорошие вещи.
Однако, если разработчик нажимает Ctrl + C во второй раз, npm ведет себя по-другому. Кажется, что он отправляет SIGTERM процессу sh
, который он использовал для запуска моего сценария start
. Это приводит к тому, что sh
процесс выводит на печать Terminated
и завершает работу, возвращая управление терминалом пользователю, не дожидаясь выхода из процесса узла.
Это действительно раздражает, поскольку создает впечатление, что процесс узла был остановлен, но, конечно, это не так. Это будет продолжаться до тех пор, пока не будет завершено отключение, или оно не будет принудительно убито чем-то вроде SIGKILL или SIGQUIT. Если случится, что что-то будет выведено на консоль, это будет сделано прямо посреди того, что еще разработчик может теперь запустить в этом терминале.
Для тривиального примера попробуйте следующее:
пакет. json:
{
"private": true,
"scripts": {
"start": "node index.js"
}
}
index. js:
async function waitForever() {
while(true) {
console.log('waiting...');
await new Promise((resolve) => {
setTimeout(resolve, 5000);
});
}
}
process.on('SIGINT', () => {
console.log('SIGINT recieved');
});
process.on('SIGTERM', () => {
console.log('SIGTERM recieved');
})
waitForever();
Запустите npm start
в своем терминале, затем нажмите Ctrl + c один раз. Вы увидите, как сигнал проходит через узел, но, конечно, он не выйдет. Теперь сделайте это во второй раз. Вы увидите, что сигнал пройдет снова, но затем вы сразу увидите «Прервано», а затем приглашение вашей оболочки. Пока вы не найдете идентификатор процесса узла и не убьете его с помощью kill -9
, вы будете продолжать видеть это сообщение waiting...
каждые пять секунд.
Я немного поигрался с этим примером, и он очень похоже, что npm полностью ответственен за это. Если вы отправите kill -2
непосредственно в процесс npm дважды, произойдет прекращение процесса оболочки, при этом SIGINT никогда не будет получен процессом узла.
Итак, у меня есть два основных вопроса:
Какого черта здесь происходит? Я что-то упускаю из-за того, как работает моя оболочка, или это какая-то особенность, встроенная в npm run-script? Если да, где я могу найти информацию об этом? npm help run-script
ничего не показывает об этом.
Я знаю, что сценарии start
довольно распространены в подобных проектах, поэтому кажется, что кто-то другой должен столкнуться с этой проблемой. Как люди обычно справляются с этим? Я погуглил кучу людей, и трудно было найти четкий ответ.
Конечно, это не так уж сложно. Стартовый скрипт - это просто удобство, чтобы убедиться, что компиляция TS запускается перед запуском. Я могу предложить разработчикам запускать встроенное приложение непосредственно в своей оболочке после сборки или написать сценарий, который выполняет сборку и запускается вне сценария npm. Но было бы неплохо не делать этого.
На самом деле я просто озадачен и был бы признателен за некоторую помощь. Спасибо!