diff --git a/config_default.js b/config_default.js index 59a20f3..fa3d29b 100644 --- a/config_default.js +++ b/config_default.js @@ -7,6 +7,12 @@ // Тело модуля //------------ +//Общие параметры +let common = { + //Таймаут останова сервера (мс) + nTerminateTimeout: 60000 +}; + //Параметры подключения к БД let dbConnect = { //Пользователь БД @@ -60,6 +66,7 @@ let mail = { //----------------- module.exports = { + common, dbConnect, outGoing, inComing, diff --git a/core/app.js b/core/app.js index 20fd25f..d9c878b 100644 --- a/core/app.js +++ b/core/app.js @@ -40,6 +40,8 @@ class ParusAppServer { this.notifier = null; //Флаг остановки сервера this.bStopping = false; + //Таймаут останова сервера + this.terminateTimeout = null; //Список обслуживаемых сервисов this.services = []; //Привяжем методы к указателю на себя для использования в обработчиках событий @@ -175,16 +177,14 @@ class ParusAppServer { await this.logger.warn("Отключение сервера приложений от БД..."); try { await this.dbConn.disconnect(); - process.exit(0); } catch (e) { 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(); } //Останов сервера - async stop() { + async stop(terminateTimeout) { if (!this.bStopping) { //Установим флаг - остановка в процессе this.bStopping = true; + //Запомним таймер аварийного останова + this.terminateTimeout = terminateTimeout; //Сообщаем, что начался останов сервера await this.logger.warn("Останов сервера приложений..."); //Останов обслуживания очереди исходящих diff --git a/index.js b/index.js index e47000c..a90c1e9 100644 --- a/index.js +++ b/index.js @@ -10,7 +10,8 @@ require("module-alias/register"); //Поддержка псевонимов при подключении модулей const cfg = require("./config"); //Настройки сервера приложений 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 => { //Сообщим о завершении процесса - appSrv.logger.warn("Сервер приложений остановлен (код: " + code + ") "); + console.log( + SCONSOLE_LOG_COLOR_PATTERN_WRN, + `${getNowString()} ПРЕДУПРЕЖДЕНИЕ: `, + `Сервер приложений остановлен (код: ${code})` + ); }); -//Перехват CTRL + C (останов процесса) -process.on("SIGINT", async () => { - appSrv.logger.warn("Получен сигнал на останов сервера приложений: SIGINT"); - //Инициируем выход из процесса - await appSrv.stop(); -}); - -//Перехват CTRL + \ (останов процесса) -process.on("SIGQUIT", () => { - appSrv.logger.warn("Получен сигнал на останов сервера приложений: SIGQUIT"); - //Инициируем выход из процесса - appSrv.stop(); -}); - -//Перехват мягкого останова процесса -process.on("SIGTERM", () => { - appSrv.logger.warn("Получен сигнал на останов сервера приложений: SIGTERM"); - //Инициируем выход из процесса - appSrv.stop(); -}); - -//Грубый останов процесса (здесь сделать ничего нельзя, но мы пытаемся) -process.on("SIGKILL", () => { - appSrv.logger.warn("Получен сигнал на останов сервера приложений: SIGKILL"); - //Инициируем выход из процесса - appSrv.stop(); +["SIGINT", "SIGQUIT", "SIGTERM"].forEach(sSig => { + process.on(sSig, async () => { + await appSrv.logger.warn(`Получен сигнал на останов сервера приложений: ${sSig}`); + const terminateTimeout = setTimeout(() => { + console.log( + SCONSOLE_LOG_COLOR_PATTERN_ERR, + `${getNowString()} ОШИБКА: `, + `Истекло время ожидания останова сервера приложений. Инициирован аварийный выход из процесса.` + ); + process.exit(1); + }, cfg.common.nTerminateTimeout); + try { + await appSrv.stop(terminateTimeout); + } catch (e) { + console.log(e); + await appSrv.logger.error(`При останове сервера приложений: ${makeErrorText(e)}`); + clearTimeout(terminateTimeout); + process.exit(1); + } + }); }); //Перехват всех неохваченных ошибок process.on("uncaughtException", e => { //Протоколируем ошибку - appSrv.logger.error(makeErrorText(e)); - //Инициируем выход из процесса - appSrv.stop(); + console.log(SCONSOLE_LOG_COLOR_PATTERN_ERR, `${getNowString()} НЕПРЕДВИДЕННАЯ ОШИБКА: `, makeErrorText(e)); + //Останов с ошибкой + process.exit(1); }); //Запуск сервера приложений @@ -82,7 +81,7 @@ const start = async () => { await appSrv.stop(); } catch (e) { //Могут быть ошибки и при остановке - это аварийный выход - appSrv.logger.error(makeErrorText(e)); + await appSrv.logger.error(makeErrorText(e)); process.exit(1); } } diff --git a/models/obj_config.js b/models/obj_config.js index 4386501..18f42bd 100644 --- a/models/obj_config.js +++ b/models/obj_config.js @@ -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); @@ -25,6 +28,22 @@ const validateInComingPort = val => val >= 0 && val <= 65535 && Number.isInteger //Функция проверки значения порта сервера обслуживания входящих сообщений 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({ //Пользователь БД @@ -209,6 +228,14 @@ const mail = new Schema({ //Схема валидации файла конфигурации const config = new Schema({ + //Общие параметры + common: { + schema: common, + required: true, + message: { + required: path => `Не указаны общие параметры конфигурации сервера приложений (${path})` + } + }, //Параметры подключения к БД dbConnect: { schema: dbConnect, @@ -247,6 +274,8 @@ const config = new Schema({ // Интерфейс модуля //------------------ +//Схема валидации общих параметров сервера приложений +exports.common = common; //Схема валидации записи журнала работы сервиса обмена exports.dbConnect = dbConnect; //Схема валидации параметров обработки очереди исходящих сообщений