Изменен алгоритм останова сервера - добавлен таймаут останова, экземпляр ParusAppServer больше не управляет процессом (убрал process.exit), переписан перехват сигналов останова

This commit is contained in:
Mikhail Chechnev 2019-07-04 22:04:39 +03:00
parent f01ee89790
commit dd9f5cddbb
4 changed files with 76 additions and 39 deletions

View File

@ -7,6 +7,12 @@
// Тело модуля // Тело модуля
//------------ //------------
//Общие параметры
let common = {
//Таймаут останова сервера (мс)
nTerminateTimeout: 60000
};
//Параметры подключения к БД //Параметры подключения к БД
let dbConnect = { let dbConnect = {
//Пользователь БД //Пользователь БД
@ -60,6 +66,7 @@ let mail = {
//----------------- //-----------------
module.exports = { module.exports = {
common,
dbConnect, dbConnect,
outGoing, outGoing,
inComing, inComing,

View File

@ -40,6 +40,8 @@ class ParusAppServer {
this.notifier = null; this.notifier = null;
//Флаг остановки сервера //Флаг остановки сервера
this.bStopping = false; this.bStopping = false;
//Таймаут останова сервера
this.terminateTimeout = null;
//Список обслуживаемых сервисов //Список обслуживаемых сервисов
this.services = []; this.services = [];
//Привяжем методы к указателю на себя для использования в обработчиках событий //Привяжем методы к указателю на себя для использования в обработчиках событий
@ -175,16 +177,14 @@ class ParusAppServer {
await this.logger.warn("Отключение сервера приложений от БД..."); await this.logger.warn("Отключение сервера приложений от БД...");
try { try {
await this.dbConn.disconnect(); await this.dbConn.disconnect();
process.exit(0);
} catch (e) { } catch (e) {
await this.logger.error(`Ошибка отключения от БД: ${e.sCode}: ${e.sMessage}`); await this.logger.error(`Ошибка отключения от БД: ${e.sCode}: ${e.sMessage}`);
process.exit(1);
} }
} else { //Мы закончили останов - сброс таймера аварийного останова, процесс завершится самостоятельно
process.exit(0); if (this.terminateTimeout) {
clearTimeout(this.terminateTimeout);
}
} }
} else {
process.exit(0);
} }
} }
//Инициализация сервера //Инициализация сервера
@ -252,10 +252,12 @@ class ParusAppServer {
await this.dbConn.connect(); await this.dbConn.connect();
} }
//Останов сервера //Останов сервера
async stop() { async stop(terminateTimeout) {
if (!this.bStopping) { if (!this.bStopping) {
//Установим флаг - остановка в процессе //Установим флаг - остановка в процессе
this.bStopping = true; this.bStopping = true;
//Запомним таймер аварийного останова
this.terminateTimeout = terminateTimeout;
//Сообщаем, что начался останов сервера //Сообщаем, что начался останов сервера
await this.logger.warn("Останов сервера приложений..."); await this.logger.warn("Останов сервера приложений...");
//Останов обслуживания очереди исходящих //Останов обслуживания очереди исходящих

View File

@ -10,7 +10,8 @@
require("module-alias/register"); //Поддержка псевонимов при подключении модулей require("module-alias/register"); //Поддержка псевонимов при подключении модулей
const cfg = require("./config"); //Настройки сервера приложений const cfg = require("./config"); //Настройки сервера приложений
const app = require("./core/app"); //Сервер приложений const app = require("./core/app"); //Сервер приложений
const { makeErrorText } = require("./core/utils"); //Вспомогательные функции const { makeErrorText, getNowString } = require("./core/utils"); //Вспомогательные функции
const { SCONSOLE_LOG_COLOR_PATTERN_ERR, SCONSOLE_LOG_COLOR_PATTERN_WRN } = require("./core/constants"); //Общие константы
//-------------------------- //--------------------------
// Глобальные идентификаторы // Глобальные идентификаторы
@ -28,43 +29,41 @@ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
//Обработка события "выход" жизненного цикла процесса //Обработка события "выход" жизненного цикла процесса
process.on("exit", code => { process.on("exit", code => {
//Сообщим о завершении процесса //Сообщим о завершении процесса
appSrv.logger.warn("Сервер приложений остановлен (код: " + code + ") "); console.log(
SCONSOLE_LOG_COLOR_PATTERN_WRN,
`${getNowString()} ПРЕДУПРЕЖДЕНИЕ: `,
`Сервер приложений остановлен (код: ${code})`
);
}); });
//Перехват CTRL + C (останов процесса) ["SIGINT", "SIGQUIT", "SIGTERM"].forEach(sSig => {
process.on("SIGINT", async () => { process.on(sSig, async () => {
appSrv.logger.warn("Получен сигнал на останов сервера приложений: SIGINT"); await appSrv.logger.warn(`Получен сигнал на останов сервера приложений: ${sSig}`);
//Инициируем выход из процесса const terminateTimeout = setTimeout(() => {
await appSrv.stop(); console.log(
}); SCONSOLE_LOG_COLOR_PATTERN_ERR,
`${getNowString()} ОШИБКА: `,
//Перехват CTRL + \ (останов процесса) `Истекло время ожидания останова сервера приложений. Инициирован аварийный выход из процесса.`
process.on("SIGQUIT", () => { );
appSrv.logger.warn("Получен сигнал на останов сервера приложений: SIGQUIT"); process.exit(1);
//Инициируем выход из процесса }, cfg.common.nTerminateTimeout);
appSrv.stop(); try {
}); await appSrv.stop(terminateTimeout);
} catch (e) {
//Перехват мягкого останова процесса console.log(e);
process.on("SIGTERM", () => { await appSrv.logger.error(`При останове сервера приложений: ${makeErrorText(e)}`);
appSrv.logger.warn("Получен сигнал на останов сервера приложений: SIGTERM"); clearTimeout(terminateTimeout);
//Инициируем выход из процесса process.exit(1);
appSrv.stop(); }
}); });
//Грубый останов процесса (здесь сделать ничего нельзя, но мы пытаемся)
process.on("SIGKILL", () => {
appSrv.logger.warn("Получен сигнал на останов сервера приложений: SIGKILL");
//Инициируем выход из процесса
appSrv.stop();
}); });
//Перехват всех неохваченных ошибок //Перехват всех неохваченных ошибок
process.on("uncaughtException", e => { process.on("uncaughtException", e => {
//Протоколируем ошибку //Протоколируем ошибку
appSrv.logger.error(makeErrorText(e)); console.log(SCONSOLE_LOG_COLOR_PATTERN_ERR, `${getNowString()} НЕПРЕДВИДЕННАЯ ОШИБКА: `, makeErrorText(e));
//Инициируем выход из процесса //Останов с ошибкой
appSrv.stop(); process.exit(1);
}); });
//Запуск сервера приложений //Запуск сервера приложений
@ -82,7 +81,7 @@ const start = async () => {
await appSrv.stop(); await appSrv.stop();
} catch (e) { } catch (e) {
//Могут быть ошибки и при остановке - это аварийный выход //Могут быть ошибки и при остановке - это аварийный выход
appSrv.logger.error(makeErrorText(e)); await appSrv.logger.error(makeErrorText(e));
process.exit(1); process.exit(1);
} }
} }

View File

@ -13,6 +13,9 @@ const Schema = require("validate"); //Схемы валидации
// Тело модуля // Тело модуля
//------------- //-------------
//Функция проверки значения таймаута останова сервера
const validateTerminateTimeout = val => val >= 1000 && val <= 120000 && Number.isInteger(val);
//Функция проверки значения размера блока одновременно обрабатываемых исходящих сообщений //Функция проверки значения размера блока одновременно обрабатываемых исходящих сообщений
const validateMaxWorkers = val => val >= 1 && val <= 100 && Number.isInteger(val); const validateMaxWorkers = val => val >= 1 && val <= 100 && Number.isInteger(val);
@ -25,6 +28,22 @@ const validateInComingPort = val => val >= 0 && val <= 65535 && Number.isInteger
//Функция проверки значения порта сервера обслуживания входящих сообщений //Функция проверки значения порта сервера обслуживания входящих сообщений
const validateMsgMaxSize = val => val >= 1 && val <= 1000 && Number.isInteger(val); const validateMsgMaxSize = val => val >= 1 && val <= 1000 && Number.isInteger(val);
//Схема валидации общих параметров сервера приложений
const common = new Schema({
//Таймаут останова сервера (мс)
nTerminateTimeout: {
type: Number,
required: true,
use: { validateTerminateTimeout },
message: {
type: path => `Таймаут останова сервера (${path}) имеет некорректный тип данных (ожидалось - Number)`,
required: path => `Не указан таймаут останова сервера (${path})`,
validateTerminateTimeout: path =>
`Таймаут останова сервера (${path}) должен быть целым числом в диапазоне от 1000 до 120000`
}
}
});
//Схема валидации параметров подключения к БД //Схема валидации параметров подключения к БД
const dbConnect = new Schema({ const dbConnect = new Schema({
//Пользователь БД //Пользователь БД
@ -209,6 +228,14 @@ const mail = new Schema({
//Схема валидации файла конфигурации //Схема валидации файла конфигурации
const config = new Schema({ const config = new Schema({
//Общие параметры
common: {
schema: common,
required: true,
message: {
required: path => `Не указаны общие параметры конфигурации сервера приложений (${path})`
}
},
//Параметры подключения к БД //Параметры подключения к БД
dbConnect: { dbConnect: {
schema: dbConnect, schema: dbConnect,
@ -247,6 +274,8 @@ const config = new Schema({
// Интерфейс модуля // Интерфейс модуля
//------------------ //------------------
//Схема валидации общих параметров сервера приложений
exports.common = common;
//Схема валидации записи журнала работы сервиса обмена //Схема валидации записи журнала работы сервиса обмена
exports.dbConnect = dbConnect; exports.dbConnect = dbConnect;
//Схема валидации параметров обработки очереди исходящих сообщений //Схема валидации параметров обработки очереди исходящих сообщений