Изменен алгоритм останова сервера - добавлен таймаут останова, экземпляр 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 = {
//Пользователь БД
@ -60,6 +66,7 @@ let mail = {
//-----------------
module.exports = {
common,
dbConnect,
outGoing,
inComing,

View File

@ -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("Останов сервера приложений...");
//Останов обслуживания очереди исходящих

View File

@ -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);
}
}

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);
@ -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;
//Схема валидации параметров обработки очереди исходящих сообщений