Как быстро приступить к работе с VUE3.0: приступить к обучению.
После выпуска нашей службы она неизбежно будет запланирована рабочей средой (например, контейнерами, pm2 и т. д.), обновления службы вызовут перезапуски, а также возникнут различные исключения. процесс может привести к сбою; в целом, работающая среда имеет мониторинг состояния сервисного процесса, который перезапустит процесс, если процесс ненормальный. При обновлении также существует стратегия последовательного обновления. Однако стратегия планирования работающей среды рассматривает наш процесс обслуживания как черный ящик и не заботится о внутренних условиях выполнения процесса обслуживания. Поэтому нашему процессу обслуживания необходимо активно отслеживать действия планирования работающей среды, а затем выполнять их. некоторые действия по очистке выхода.
Итак, сегодня мы разберем различные ситуации, которые могут привести к завершению процесса Node.js, и что мы можем сделать, прослушивая эти события завершения процесса.
Принцип:
когда процесс хочет завершить работу, существует не более двух ситуаций. Одна из них заключается в том, что процесс активно завершает работу, а другая заключается в том, что он получает системный сигнал, требующий завершения процесса.
Выход из уведомления о системном сигнале
. Общие системные сигналы перечислены в официальном документе Node.js. В основном мы фокусируемся на нескольких:
При получении сигнала непринудительного выхода процесс Node.js может прослушивать сигнал выхода и выполнять некоторую пользовательскую логику выхода. Например, мы написали инструмент cli, выполнение задачи которого занимает много времени. Если пользователь хочет выйти из процесса с помощью ctrl+c до завершения задачи, ему может быть предложено подождать:
const readline = require(' линия чтения'); process.on('SIGINT', () => { // Мы используем readline, чтобы просто реализовать взаимодействие в командной строке const rl = readline.createInterface({ ввод: процесс.stdin, вывод: процесс.stdout }); rl.question('Задание еще не выполнено, вы уверены, что хотите выйти?', ответ => { если (ответ === 'да') { console.log('Выполнение задачи прервано, выходим из процесса'); процесс.выход(0); } еще { console.log('Задача продолжается...'); } рл.закрыть(); }); }); // Имитируем задачу, выполнение которой занимает 1 минуту const longTimeTask = () => { console.log('Запуск задачи...'); setTimeout(() => { console.log('окончание задачи'); }, 1000*60); }; longTimeTask();
Эффект следующий: при каждом нажатии ctrl + c пользователю будет предложено:
Процесс активно завершает работу
Node.js. Процесс активно завершается, в основном, в следующих ситуациях:
Мы знаем, что pm2 имеет эффект демона. В вашем случае, когда ваш процесс завершается с ошибкой, pm2 перезапускает ваш процесс. дочерний процесс в кластерном режиме Node.js (на самом деле pm2 имеет аналогичную логику):
const cluster = require('cluster' ); const http = require('http'); const numCPUs = require('os').cpus().length; константный процесс = требуется ('процесс'); //Основной код процесса if (cluster.isMaster) { console.log(`Запустите основной процесс: ${process.pid}`); // На основе количества ядер процессора создаем рабочий процесс для (let i = 0; i < numCPUs; i++) { кластер.вилка(); } //Прослушиваем событие выхода рабочего процесса Cluster.on('exit', (worker, code, signal) => { console.log(`Рабочий процесс ${worker.process.pid} завершен, код ошибки: ${code || signal}, перезапуск...`); //Перезапускаем дочерний процесс Cluster.fork(); }); } // Код рабочего процесса if (cluster.isWorker) { // Прослушиваем события неперехваченных ошибокprocess.on('uncaughtException', error => { console.log(`Произошла ошибка в рабочем процессе ${process.pid}`, error); процесс.emit('отключить'); процесс.выход(1); }); //Создаем веб-сервер // Каждый рабочий процесс будет прослушивать порт 8000 (Node.js будет обрабатывать его внутри и не вызывать конфликтов портов) http.createServer((req, res) => { res.writeHead(200); res.end('привет, мирn'); }).слушать(8000); console.log(`Запустить рабочий процесс: ${process.pid}`); }
Практика применения
Различные ситуации, в которых процесс Node.js завершается, были проанализированы выше. Теперь мы создадим инструмент для мониторинга выхода процесса. Когда процесс Node.js завершается, пользователю разрешается выполнить свою собственную логику выхода:
// выход-хук.js. //Сохраняем задачи выхода, которые необходимо выполнить const Tasks = []; //Добавляем задачу выхода const addExitTask = fn => Tasks.push(fn); const handleExit = (код, ошибка) => { // ...реализация handleExit показана ниже}; //Прослушиваем различные события выходаprocess.on('exit', code => handleExit(code)); // Согласно спецификациям POSIX, мы используем 128 + номер сигнала для получения окончательного кода выхода // Номер сигнала указан на рисунке ниже. В системе Linux можно выполнить команду kill -l, чтобы просмотреть все номера сигналов. ('SIGHUP', () => handleExit(128 + 1)); process.on('SIGINT', () => handleExit(128 + 2)); process.on('SIGTERM', () => handleExit(128 + 15)); // Нажмите ctrl+break, чтобы выйти из сигнального процесса.on('SIGBREAK', () => handleExit(128 + 21)); // Код выхода 1 представляет собой неперехваченную ошибку, из-за которой процесс завершился.process.on('uncaughtException', error => handleExit(1, error)); process.on('unhandledRejection', error => handleExit(1, error))
;
Далее нам нужно реализовать функцию завершения реального процесса handleExit, поскольку функция задачи, передаваемая пользователем, может быть синхронной или асинхронной, мы можем использоватьprocess.nextTick, чтобы гарантировать, что пользовательский код синхронизации был выполнен, что можно легко понять; .nextTick будет выполнен после завершения синхронного выполнения кода на каждом этапе цикла событий (понимайте, что для асинхронных задач нам нужен процесс.nextTick, нам нужно, чтобы пользователь вызвал обратный вызов, чтобы сообщить нам, что асинхронная задача была завершена:
// Отметить, была ливыполнена);
происходит выход. Избегайте многократного выполнения let isExiting = false; const handleExit = (код, ошибка) => { если (isExiting) возврат; isExiting = правда; // Отмечаем, что действие выхода выполнено, чтобы избежать множественных вызовов let hasDoExit = fasle; const doExit = () => { если (hasDoExit) возврат; hasDoExit = истина процесс.nextTick(() => процесс.exit(код)) } // Записываем количество асинхронных задач let asyncTaskCount = 0; // После завершения асинхронной задачи обратный вызов, который необходимо вызвать пользователю, let ayncTaskCallback = () => { процесс.nextTick(() => { асинхронный тасккаунт-- если (asyncTaskCount === 0) doExit() }) } //Выполняем все задачи выхода Tasks.forEach(taskFn => { // Если количество параметров функции TaskFn больше 1, считается, что передан параметр обратного вызова и это асинхронная задача if (taskFn.length > 1) { асинктасккаунт++ TaskFn (ошибка, ayncTaskCallback) } еще { задачаFn (ошибка) } }); // Если есть асинхронная задача if (asyncTaskCount > 0) { // Спустя более 10 секунд принудительно выходим setTimeout(() => { сделатьВыход(); }, 10 * 1000) } еще { сделатьВыход() } };
На этом наш инструмент мониторинга завершения процесса завершен. Для полной реализации вы можете просмотреть эту библиотеку с открытым исходным кодом async-exit-hook
https://github.com/darukjs/daruk-exit-hook
. выходит из
нашего веб-сервера. При перезапуске, запланированном работающим контейнером (pm2 или докером и т. д.) или когда возникает исключение и процесс завершается, мы надеемся выполнить действия выхода, такие как завершение ответа на запросы, связанные с веб-сервером. службы, очистка соединения с базой данных, печать журналов ошибок, срабатывание сигналов тревоги и т. д. после завершения действия выхода и выхода из процесса мы можем прямо сейчас использовать инструмент мониторинга завершения процесса, чтобы реализовать:
const http = require(' http'); //Создаем веб-сервер const server = http.createServer((req, res) => { res.writeHead(200); res.end('привет, мирn'); }).слушать(8000); // Используйте инструмент, который мы разработали выше, чтобы добавить задачу выхода из процесса addExitTask((error, callback) => { // Печатаем журналы ошибок, запускаем сигналы тревоги, освобождаем соединения с базой данных и т. д. console.log('Процесс завершился ненормально', ошибка) // Прекратим принимать новые запросы server.close((error) => { если (ошибка) { console.log('Ошибка прекращения приема новых запросов', ошибка) } еще { console.log('Прекратить принимать новые запросы') } }) // Более простой подход — ждать в течение определенного периода времени (здесь мы ждем 5 с) завершения существующих запросов // Если вы хотите полностью гарантировать обработку всех запросов, вам нужно записывать каждое соединение и ждать, пока все соединения освобождаются. Выполните действие выхода // Вы можете обратиться к библиотеке с открытым исходным кодом https://github.com/sebhildebrandt/http-graceful-shutdown. setTimout (обратный вызов, 5 * 1000) })
Резюме
Я полагаю, что из приведенного выше текста вы уже знаете о различных ситуациях, которые приводят к завершению процесса Node.js. После того, как служба подключена к сети, хотя такие инструменты, как k8s и pm2, могут постоянно вызывать процесс, когда процесс завершается ненормально, чтобы гарантировать доступность службы, мы также должны активно определять ненормальность или планирование процесса в коде, чтобы чтобы иметь возможность выявлять проблемы раньше.