DevGang
Авторизоваться

Изящное отключение в NodeJS 

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

Изящное отключение

Давайте представим, что у вас есть HTTP-сервер с NodeJS, который подключен к базе данных, и каждый раз, когда сервер вызывается, он отправляет запрос в базу данных, чтобы получить / установить данные, которые также будут отправлены клиенту в ответе.

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

-Это заставляет задуматься, верно?

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

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

Очень важно создать корректное завершение работы и правильно завершить работу вашего сервера, потому что вы не можете знать, что может произойти с запросами, которые были сделаны к серверу, если вы немедленно выключили его, вы можете сделать ошибку и убить другой процесс (byPID), который предоставляется серверу NodeJS, или может произойти что-то другое, что может быть плохо для вашего приложения.

Как это работает

Вот четыре шага того, как вы можете сделать постепенное отключение простым способом.

  • Обрабатывать сигнал уничтожения процесса
  • Остановить новые запросы от клиента
  • Закрыть все процессы обработки данных
  • Выход из процесса

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

Давайте создадим простой сервер NodeJS и выполним все шаги, которые я упомянул выше, которые будут корректно завершаться, когда мы хотим закрыть сервер и понять, как он работает.

Вот простой пример NodeJS Server с использованием ExpressJS

const express = require('express');
const mongoose = require('mongoose');

const app = express();
app.use(express.urlencoded({extended: true})); 
app.use(express.json());

mongoose.connect('mongodb://localhost/test', (err) => {
  if (err) throw err;
  console.log('Mongoose connected!');
});
const User = mongoose.model('User', { name: String });

app.post('/user', async (req, res) => {
  try {
    const user = new User({ name: req.body.username });
    await user.save();
    res.send('Success!').status(201);
  } catch (err) {
    res.send(err.message).status(500);
  }
});

app.listen(3000, () => console.log('Example app listening on port 3000!'));

Здесь у нас есть простой сервер с маршрутом, который создает пользователя в MongoDB.

С помощью этой команды мы можем протестировать сервер и создать пользователя в базе данных.

curl -d ‘{ “username”: “Narek” }’ -H “Content-Type: application/json” -X POST http://localhost:3000/user

Если вы получили сообщение Success!, то вы можете посмотреть данные JSON в MongoDB.

Теперь давайте пройдемся по четырем шагам и напишем соответствующий код для этого.

1. Обработка сигнала уничтожения процесса

Сначала давайте разберемся, что такое сигнал процесса.

Сигнал представляет собой асинхронное уведомление отправляется в процесс или определенный поток, чтобы уведомить событие, которое произошло.

Сигнальные события будут генерироваться, когда процесс NodeJS получит сигнал.

Каждый сигнал имеет имя (например, «SIGINT», «SIGTERM» и т.д.), подробнее об этом в NodeJS здесь.

'SIGINT' генерируется с помощью + C в терминале. Сигнал 'SIGTERM' является общим сигналом, используемым для завершения программы. В отличие от 'SIGKILL' этот сигнал может быть заблокирован, обработан и проигнорирован. Это нормальный способ вежливо попросить программу завершиться. Команда оболочки kill генерирует 'SIGTERM' по умолчанию.

Вы можете прочитать больше о сигналах завершения здесь.

Как вы думаете, нам нужно добавить обработчик, который будет получать сигнал «SIGTERM».

Вот следующий пример, построенный на предыдущем примере, который обрабатывает сигнал.

const express = require('express');
const mongoose = require('mongoose');

const app = express();
app.use(express.urlencoded({extended: true})); 
app.use(express.json());

mongoose.connect('mongodb://localhost/test', (err) => {
  if (err) throw err;
  console.log('Mongoose connected!');
});
const User = mongoose.model('User', { name: String });

app.post('/user', async (req, res) => {
  try {
    const user = new User({ name: req.body.username });
    await user.save();
    res.send('Success!').status(201);
  } catch (err) {
    res.send(err.message).status(500);
  }
});

app.listen(3000, () => console.log('Example app listening on port 3000!'));

process.on('SIGTERM', () => {
  console.info('SIGTERM signal received.');
});

Теперь давайте попробуем и проверим это.

Запустите сервер, после этого вам нужно получить номер PID, вы получите его с помощью команды ps, так что теперь у вас есть номер, и вы можете попытаться убить сервер, используя эту команду kill [PID_number] или просто killall node, который отправит сигнал на все серверы node&

SIGTERM сигнал получен.

Если вы попытаетесь снова, вы получите тот же лог

SIGTERM сигнал получен.

-Хммм, почему процесс не был убит?

Потому что вы обработали сигнал и проигнорировали его.

Итак, первый шаг сделан, давайте перейдем к следующему шагу.

2. Остановка новых запросов от клиента

Теперь нам нужно остановить http-сервер и перестать принимать новые запросы.

Это можно сделать с помощью функции server.close, чтобы получить больше информации можете посетить страницу документации NodeJS.

Наш код будет выглядеть так

const express = require('express');
const mongoose = require('mongoose');

const app = express();
app.use(express.urlencoded({extended: true})); 
app.use(express.json());

mongoose.connect('mongodb://localhost/test', (err) => {
  if (err) throw err;
  console.log('Mongoose connected!');
});
const User = mongoose.model('User', { name: String });

app.post('/user', async (req, res) => {
  try {
    const user = new User({ name: req.body.username });
    await user.save();
    res.send('Success!').status(201);
  } catch (err) {
    res.send(err.message).status(500);
  }
});

const server = app.listen(3000, () => console.log('Example app listening on port 3000!'));

process.on('SIGTERM', () => {
  console.info('SIGTERM signal received.');
  console.log('Closing http server.');
  server.close(() => {
    console.log('Http server closed.');
  });
});

Он прекратит прием новых подключений к серверу, и если вы попытаетесь вызвать сервер, ваш запрос не будет выполнен.

3. Закрытие всех текущих процессов

В этом примере смысл заключается в том, чтобы закрыть соединение MongoDB, поскольку к базе данных не осталось запросов.

Так что это можно сделать с помощью этого кода

const express = require('express');
const mongoose = require('mongoose');

const app = express();
app.use(express.urlencoded({extended: true})); 
app.use(express.json());

mongoose.connect('mongodb://localhost/test', (err) => {
  if (err) throw err;
  console.log('Mongoose connected!');
});
const User = mongoose.model('User', { name: String });

app.post('/user', async (req, res) => {
  try {
    const user = new User({ name: req.body.username });
    await user.save();
    res.send('Success!').status(201);
  } catch (err) {
    res.send(err.message).status(500);
  }
});

const server = app.listen(3000, () => console.log('Example app listening on port 3000!'));

process.on('SIGTERM', () => {
  console.info('SIGTERM signal received.');
  console.log('Closing http server.');
  server.close(() => {
    console.log('Http server closed.');
    // boolean means [force], see in mongoose doc
    mongoose.connection.close(false, () => {
      console.log('MongoDb connection closed.');
    });
  });
});

-Хммммм, почему Node сервер выходит после закрытия соединения с БД.

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

4. Выход из процесса

Как вы уже видели, наше приложение закрывает соединение с базой данных.

В чем причина? - EventLoop

Как мы знаем, NodeJS выйдет, когда очередь EventLoop пуста и ничего не останется.

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

И последний пример изящного выключения сервера будет выглядеть так

const express = require('express');
const mongoose = require('mongoose');

const app = express();
app.use(express.urlencoded({extended: true})); 
app.use(express.json());

mongoose.connect('mongodb://localhost/test', (err) => {
  if (err) throw err;
  console.log('Mongoose connected!');
});
const User = mongoose.model('User', { name: String });

app.post('/user', async (req, res) => {
  try {
    const user = new User({ name: req.body.username });
    await user.save();
    res.send('Success!').status(201);
  } catch (err) {
    res.send(err.message).status(500);
  }
});

const server = app.listen(3000, () => console.log('Example app listening on port 3000!'));

process.on('SIGTERM', () => {
  console.info('SIGTERM signal received.');
  console.log('Closing http server.');
  server.close(() => {
    console.log('Http server closed.');
    // boolean means [force], see in mongoose doc
    mongoose.connection.close(false, () => {
      console.log('MongoDb connection closed.');
      process.exit(0);
    });
  });
});

Аргумент 0 означает выход с кодом «успеха».

Для выхода скодом «сбой» используйте 1.

Чтобы получить этот код выхода после завершения работы, выполните эту команду в терминале, где вы запускаете сервер вашего Node приложения.

echo $?

По умолчанию NodeJS завершается с кодом процесса 0, если EventLoop пуст.

Резюме

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

Источник:

#NodeJS
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу