在 windows 平台上 PM2 不支持 Node.js for "Graceful Shutdown"

On windows platform PM2 not supporting Node.js for "Graceful Shutdown"

我试过这种方法-

const server = app.listen(port, () => console.log('Project running on port ' + port));

process.on('SIGINT', () => {
  console.info('SIGINT signal received.')

  // Stops the server from accepting new connections and finishes existing connections.
  server.close(function(err) {
    // if error, log and exit with error (1 code)
    if (err) {
      console.error(err)
      process.exit(1)
    }

    // close your database connection and exit with success (0 code)
    // for example with mongoose
    mongoose.connection.close(function () {
      console.log('Mongoose connection disconnected')
      process.exit(0)
    })
  })
})

第二个也是这样-

const server = app.listen(port, () => console.log('Project running on port ' + port));

process.on('message', function(msg) {

  console.info('shutdown signal received.',msg)
  if (msg == 'shutdown') {
    console.log('Closing all connections...');
    setTimeout(function() {
      console.log('Finished closing connections');
      process.exit(0);
    }, 1500);
  }
});

但两者都没有帮助。

我通过阅读所有相关的类似开放问题了解到 PM2 而不是向节点发送 'SIGINT' 信号,它自己杀死了它。

我们可以自己制作一个 bash 脚本,而不是使用 pm2,如果因某些错误而关闭,它将重新启动我们的节点项目。

    @echo off
    setlocal enableextensions enabledelayedexpansion
    set /a "x = 0"

:more_to_process
    if %x% leq 10 (
        echo %x%
        node E:\Max\Projects\dummy\API\server.js
        set /a "x = x + 1"
        goto :more_to_process
    )

    endlocal
    pause

以上脚本重试 10 次以重新启动 API。可以更改重试时间。

现在为了制作日志文件和处理错误,我们可以做

const server = app.listen(port, () => console.log('Dummy-App API running on port ' + port));

var fs = require('fs');
var util = require('util');

var path = __dirname + `/mylogfiles/debug(${getDate()}).log`, printNextLine = false
if(fs.existsSync(path)){
  printNextLine = true
}
var log_file = fs.createWriteStream(path, {flags : 'a'});  
/* 'a'  : open the file for writing, positioning the stream at the end of the file. The file is created if not existing
   'a+' : open the file for reading and writing, positioning the stream at the end of the file. The file is created if not existing
   'r+' : open the file for reading and writing
   'w+' : open the file for reading and writing, positioning the stream at the beginning of the file. The file is created if not existing

(you can find more flags at https://nodejs.org/api/fs.html#fs_file_system_flags)

*/
if(printNextLine == true){
  let prnt = '***************************************************************************************************************'
  log_file.write('\n' + '\n' + prnt + '\n');
}
var log_stdout = process.stdout;

console.log = function(d) { //
  log_file.write((new Date).toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }) + ' | ' + util.format(d) + '\n');
  log_stdout.write( util.format(d) + '\n');
};

console.error = function(d) { //
  log_file.write((new Date).toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }) + ' | ' + util.format(d) + '\n');
  log_stdout.write( util.format(d) + '\n');
};

process.on('uncaughtException', function (err) {
  console.error(err)
  // console.error((new Date).toUTCString() + ' uncaughtException:', err.message)
  // console.error(err.stack)
  process.exit(1)
})

function getDate(addDay){
    let mydate = new Date()
    var month = mydate.getMonth() + 1; // month (in integer 0-11)
    var year = mydate.getFullYear(); // year
    var date = mydate.getDate()
    let h = mydate.getHours() 
    let m = mydate.getMinutes()
    let s = mydate.getSeconds() 
    // let desiredDate = `${year}.${month}.${date} H-${h} M-${m} S-${s}`
    let desiredDate = `${year}.${month}.${date}`
    return desiredDate
}
请创建一个名为 'mylogfiles' 的文件夹,与 server.js/index.js 文件并行。

现在,"Graceful Shutdown"还剩下什么:-

process.on('SIGINT', () => {
  console.info('SIGINT signal received.')

  // Stops the server from accepting new connections and finishes existing connections.
  server.close(function(err) {
    // if error, log and exit with error (1 code)
    if (err) {
      console.error(err)
      process.exit(1)
    }

    // close your database connection and exit with success (0 code)
    // for example with mongoose
    mongoose.connection.close(function () {
      console.log('Mongoose connection disconnected')
      process.exit(0)
    })
  })
})

如果我们 run/start Node.js API 使用命令,关于代码 process.on('SIGINT', .. 将有所帮助 node server.js 或文件 index.js 已命名。

这就是我们构建 Bash 脚本的原因,该脚本将 'node' 作为启动项目的命令-API。 可以将 bash 脚本放在 system/server 的任何位置,除非并直到 bash 脚本上给出的用于启动 API 主文件的路径是正确的。

最后,主服务器.js/index.js 文件可能看起来像 -

const express = require('express');
const bodyParser = require('body-parser'); // call body-parser here to parse body request from frontend 
var fileUpload = require('express-fileupload');  // call express-fileupload here to accept file in the form of multipart data  from frontend 

// create express app
const app = express();

// parse requests of content-type - application/json
app.use(bodyParser.json({limit:'50mb'}));  // here we try to set body request can accept requested data from frontend upto 50Mb

// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true,limit:'50mb' })); // here we try to set body request can accept requested data from frontend as urlencoded and upto 50Mb
app.use(fileUpload()); // here enable app to use fileupload

// Configuring the database
const dbConfig = require('./config/database.config.js');

const mongoose = require('mongoose');

mongoose.Promise = global.Promise;

const options = {
  auth: {
    user: "*********ww",
    password: "******dd"
  },
  useNewUrlParser: true,
  useCreateIndex: true,
  useFindAndModify: false,
  autoIndex: true, // Don't build indexes
  reconnectTries: Number.MAX_VALUE, // Never stop trying to reconnect
  reconnectInterval: 500, // Reconnect every 500ms
  poolSize: 10, // Maintain up to 10 socket connections
  // If not connected, return errors immediately rather than waiting for reconnect
  bufferMaxEntries: 0,
  connectTimeoutMS: 10000, // Give up initial connection after 10 seconds
  socketTimeoutMS: 45000, // Close sockets after 45 seconds of inactivity
  family: 4, // Use IPv4, skip trying IPv6
  serverSelectionTimeoutMS: 5000, // Timeout after 5s instead of 30s
  useUnifiedTopology: true
};
mongoose.connect(dbConfig.url, options).then(
    () => { 
        console.log("Successfully connected to the database");
    },
    err => { 
        /** handle initial connection error */ 
        console.log('Could not connect to the database. Exiting now...', err);
        process.exit();
    }
);

var port = process.env.PORT || 23804;        // set our port and port must be enable on server

var router = express.Router();    // call Router methode to enable api can accept multiple routes  

app.all('/*', function(req, res,next) {

  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Credentials", "true");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-Content-Type-Options, X-XSS-Protection, Content-Type, Accept, Key, Authorization");
  res.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH");
  next()

});

// define a simple route
app.get('/', (req, res) => {
    res.json({"message": "Welcome to Dummy application. Sign Up and Login Quicky. Organize and keep track of all forms."});
});

// Require  routes

require('./app/routes/controlFile.routes.js')(app);

const server = app.listen(port, () => console.log('Dummy-App API running on port ' + port));

var fs = require('fs');
var util = require('util');

var path = __dirname + `/mylogfiles/debug(${getDate()}).log`, printNextLine = false
if(fs.existsSync(path)){
  printNextLine = true
}
var log_file = fs.createWriteStream(path, {flags : 'a'});  
/* 'a'  : open the file for writing, positioning the stream at the end of the file. The file is created if not existing
   'a+' : open the file for reading and writing, positioning the stream at the end of the file. The file is created if not existing
   'r+' : open the file for reading and writing
   'w+' : open the file for reading and writing, positioning the stream at the beginning of the file. The file is created if not existing

(you can find more flags at https://nodejs.org/api/fs.html#fs_file_system_flags)

*/
if(printNextLine == true){
  let prnt = '***************************************************************************************************************'
  log_file.write('\n' + '\n' + prnt + '\n');
}
var log_stdout = process.stdout;

console.log = function(d) { //
  log_file.write((new Date).toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }) + ' | ' + util.format(d) + '\n');
  log_stdout.write( util.format(d) + '\n');
};

console.error = function(d) { //
  log_file.write((new Date).toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }) + ' | ' + util.format(d) + '\n');
  log_stdout.write( util.format(d) + '\n');
};

process.on('uncaughtException', function (err) {
  console.error(err)
  // console.error((new Date).toUTCString() + ' uncaughtException:', err.message)
  // console.error(err.stack)
  process.exit(1)
})

process.on('SIGINT', () => {
  console.info('SIGINT signal received.')

  // Stops the server from accepting new connections and finishes existing connections.
  server.close(function(err) {
    // if error, log and exit with error (1 code)
    if (err) {
      console.error(err)
      process.exit(1)
    }

    // close your database connection and exit with success (0 code)
    // for example with mongoose
    mongoose.connection.close(function () {
      console.log('Mongoose connection disconnected')
      process.exit(0)
    })
  })
})

function getDate(addDay){
    let mydate = new Date()
    var month = mydate.getMonth() + 1; // month (in integer 0-11)
    var year = mydate.getFullYear(); // year
    var date = mydate.getDate()
    let h = mydate.getHours() 
    let m = mydate.getMinutes()
    let s = mydate.getSeconds() 
    // let desiredDate = `${year}.${month}.${date} H-${h} M-${m} S-${s}`
    let desiredDate = `${year}.${month}.${date}`
    return desiredDate
}