Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/42.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Node.js 当Express服务器的进程被终止时,如何优雅地关闭它?_Node.js_Express - Fatal编程技术网

Node.js 当Express服务器的进程被终止时,如何优雅地关闭它?

Node.js 当Express服务器的进程被终止时,如何优雅地关闭它?,node.js,express,Node.js,Express,在生产环境中运行Express应用程序时,我希望在服务器进程终止时(即发送SIGTERM或SIGINT)正常关闭服务器 以下是我的代码的简化版本: const express = require('express'); const app = express(); app.get('/', (req, res) => res.json({ ping: true })); const server = app.listen(3000, () => console.log('Run

在生产环境中运行Express应用程序时,我希望在服务器进程终止时(即发送SIGTERM或SIGINT)正常关闭服务器

以下是我的代码的简化版本:

const express = require('express');

const app = express();

app.get('/', (req, res) => res.json({ ping: true }));

const server = app.listen(3000, () => console.log('Running…'));

setInterval(() => server.getConnections(
    (err, connections) => console.log(`${connections} connections currently open`)
), 1000);

process.on('SIGTERM', shutDown);
process.on('SIGINT', shutDown);

function shutDown() {
    console.log('Received kill signal, shutting down gracefully');
    server.close(() => {
        console.log('Closed out remaining connections');
        process.exit(0);
    });

    setTimeout(() => {
        console.error('Could not close connections in time, forcefully shutting down');
        process.exit(1);
    }, 10000);
}
当我运行它并在浏览器中调用URL时,setInterval函数中的log语句将一直打印“1连接当前打开”,直到我实际关闭浏览器窗口。很明显,即使关闭选项卡也会保持连接打开

因此,当我按下Ctrl+C键终止服务器时,它将进入超时状态,并在10秒后打印“无法关闭连接”,同时继续打印“1连接打开”

只有在终止进程之前关闭浏览器窗口,才会收到“关闭剩余连接”消息


我在这里错过了什么?正常关闭Express服务器的正确方法是什么?

尝试NPM,正常关闭将允许完成任何连接,包括与数据库的连接,而不允许建立任何新的/新的连接。由于您使用的是express,这可能是您正在寻找的模块,但是快速的NPM搜索将显示适合Http服务器等的完整模块列表。

如果有人感兴趣,我自己找到了一个解决方案(希望在评论中听到反馈)

我为服务器上打开的连接添加了一个侦听器,将对这些连接的引用存储在一个数组中。当连接关闭时,它们将从阵列中删除

当服务器被终止时,通过调用其
end
方法关闭每个连接。对于某些浏览器(例如Chrome),这是不够的,因此在超时后,我在每个连接上调用
destroy

const express = require('express');

const app = express();

app.get('/', (req, res) => res.json({ ping: true }));

const server = app.listen(3000, () => console.log('Running…'));

setInterval(() => server.getConnections(
    (err, connections) => console.log(`${connections} connections currently open`)
), 1000);

process.on('SIGTERM', shutDown);
process.on('SIGINT', shutDown);

let connections = [];

server.on('connection', connection => {
    connections.push(connection);
    connection.on('close', () => connections = connections.filter(curr => curr !== connection));
});

function shutDown() {
    console.log('Received kill signal, shutting down gracefully');
    server.close(() => {
        console.log('Closed out remaining connections');
        process.exit(0);
    });

    setTimeout(() => {
        console.error('Could not close connections in time, forcefully shutting down');
        process.exit(1);
    }, 10000);

    connections.forEach(curr => curr.end());
    setTimeout(() => connections.forEach(curr => curr.destroy()), 5000);
}

您遇到的问题是,所有现代浏览器都会对多个请求重复使用单个连接。这称为保持活动连接

处理此问题的正确方法是监视所有新连接和请求,并跟踪每个连接的状态(当前是空闲还是活动)。然后,您可以强制关闭所有空闲连接,并确保在处理当前请求后关闭活动连接

我已经实现了专门设计的模块,该模块可以从整体上优雅地关闭Express应用程序和节点服务器。遗憾的是,nor Express和nor Node本身都没有内置此功能

下面介绍了如何将其用于任何Express应用程序:

const express=require('express');
const GracefulShutdownManager=require(“@moebius/http优雅关机”).GracefulShutdownManager;
常量app=express();
const server=app.listen(8080);
const shutdownManager=新的GracefulShutdownManager(服务器);
process.on('SIGTERM',()=>{
关闭管理器。终止(()=>{
log('服务器正常终止');
});
});

请随意查看,GitHub页面有更多详细信息。

Express()的创建者推荐了一个开源项目

终端使用的基本示例:

const http = require('http');
const express = require('express');
const terminus = require('@godaddy/terminus');

const app = express();

app.get('/', (req, res) => {
  res.send('ok');
});

const server = http.createServer(app);

function onSignal() {
  console.log('server is starting cleanup');
  // start cleanup of resource, like databases or file descriptors
}

async function onHealthCheck() {
  // checks if the system is healthy, like the db connection is live
  // resolves, if health, rejects if not
}

terminus(server, {
  signal: 'SIGINT',
   healthChecks: {
    '/healthcheck': onHealthCheck,
  },
  onSignal
});

server.listen(3000);
terminus有很多选项,以防您需要服务器生命周期回调(即从service registry注销实例等):


正确处理操作系统信号:

优雅地关闭Express:


这些工具的组合将在关机时停止新连接,允许现有连接完成,然后最终退出

如果允许的话,还有一个更好的解决方案,使用
server destroy
包可以减少工作量。在内部,这个包将优雅地终止每个连接,然后允许服务器被“销毁”。在这种情况下,我们确保最终结束express应用程序(如果使用调用函数,则可能再次启动它)。这对我使用electron是有效的,并且可以潜在地移植到标准服务器:

const express = require('express')
const { ipcMain } = require('electron')
const enableDestroy = require('server-destroy')
const port = process.env.PORT || 3000

export const wsServer = () => {
  try {
    let app = null
    let server = null

    const startServer = () => {
      if (app) {
        app = null
      }

      app = express()
      app.use(express.static('public'))
      app.use('/', (req, res) => {
        res.send('hello!')
      })

      server = app.listen(3000, () => {
        console.log('websocket server is ready.')
        console.log(`Running webserver on http://localhost:${port}`)
      })

      enableDestroy(server)
    }

    const stopServer = () => {
      if (server !== null) {
        server.destroy()
        app = null
        server = null
      }
    }
    const restartServer = () => {
      stopServer()
      startServer()
    }

    ipcMain.on('start-socket-service', (event) => {
      startServer()
      console.log('Start Server...')
      event.returnValue = 'Service Started'
    })

    ipcMain.on('stop-socket-service', (event) => {
      stopServer()
      console.log('Stop Server...')
      event.returnValue = 'Service Stopped'
    })

    ipcMain.on('restart-socket-service', () => {
      restartServer()
    })

  } catch (e) {
    console.log(e)
  }
}

你能定义“优雅”吗?你认为你需要优雅地做什么?操作系统将关闭服务器、关闭所有套接字、释放所有内存等。。。当进程被终止时。所以,我们想知道你还需要做什么才能做出一个“优雅”的退出?我假设如果我没有调用Serv.Cutter,在请求中间的浏览器(通常需要100-200毫秒)会出错。server.close还有什么其他用途?您阅读了吗?我现在已经阅读了,谢谢您的输入!我尝试了优雅的shutdown模块,如果您查看它的代码,您会发现它与我的代码几乎相同:侦听SIGTERM,发生时执行server.close。当我更改代码以使用该中间件时,会发生完全相同的事情:超时时间过后,它会执行一个进程。退出(1)时,会显示一条消息“无法及时关闭连接,强制关闭”,原则上是被否决的。您链接了一个包含40行代码的NPM模块。完全不需要第三方解决方案来正确解决此问题。@PatrickHund奇怪的是,您的答案的日期晚于此处的注释或上述答案。正在调用与哪个节点(或操作系统)不同的仍然处于活动状态的套接字上的
.end()
进程何时终止?您好@Przmek Nowak,您能帮助我检查节点服务器的运行状况吗?仅适用于Windows:我在kubernetes上使用过它,并且有oknote bind命令从
terminus
更改为
createTerminus
您能验证您的链接吗?您能解释一下这与发生的情况有什么不同吗当操作系统终止节点进程时?@Purefan来自模块文档:
当应用程序的进程被操作系统中断时(通过传递SIGINT或SIGTERM信号),默认情况下,服务器立即终止,所有打开的连接都被粗暴断开。这意味着,如果某个客户端正在从服务器发送或接收数据,它将遇到错误。这很容易导致错误升级吗
const express = require('express')
const { ipcMain } = require('electron')
const enableDestroy = require('server-destroy')
const port = process.env.PORT || 3000

export const wsServer = () => {
  try {
    let app = null
    let server = null

    const startServer = () => {
      if (app) {
        app = null
      }

      app = express()
      app.use(express.static('public'))
      app.use('/', (req, res) => {
        res.send('hello!')
      })

      server = app.listen(3000, () => {
        console.log('websocket server is ready.')
        console.log(`Running webserver on http://localhost:${port}`)
      })

      enableDestroy(server)
    }

    const stopServer = () => {
      if (server !== null) {
        server.destroy()
        app = null
        server = null
      }
    }
    const restartServer = () => {
      stopServer()
      startServer()
    }

    ipcMain.on('start-socket-service', (event) => {
      startServer()
      console.log('Start Server...')
      event.returnValue = 'Service Started'
    })

    ipcMain.on('stop-socket-service', (event) => {
      stopServer()
      console.log('Stop Server...')
      event.returnValue = 'Service Stopped'
    })

    ipcMain.on('restart-socket-service', () => {
      restartServer()
    })

  } catch (e) {
    console.log(e)
  }
}