You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在运行时重启Node.js服务器?求Express等框架实现方案

嘿,这个需求我之前也折腾过,其实核心是要通过进程管理来实现运行时重启——直接在当前进程里重启Express服务器是行不通的,因为app.listen启动后没法直接重新绑定端口。下面给你两种靠谱的方案,先从你用的Express说起,再补个Hapi的例子~

Express 实现方案

生产环境推荐:父子进程管理

这种方式通过主进程控制子进程(实际运行的Express服务器),收到重启请求时优雅关闭旧子进程,再启动新的,适合生产环境使用。

首先创建server.js(实际的Express服务文件):

const express = require('express');
const app = express();

// 重启接口
app.post('/restart', (req, res) => {
  // 给主进程发送重启信号
  process.send({ type: 'restart' });
  // 先给客户端返回响应
  res.json({ message: 'Server restart initiated successfully' });
});

// 测试用的基础路由
app.get('/', (req, res) => {
  res.send('Hello from the Express server!');
});

const port = process.env.PORT || 8000;
const server = app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});

// 监听主进程的关闭指令,优雅关闭服务器
process.on('message', (msg) => {
  if (msg.type === 'shutdown') {
    server.close(() => {
      console.log('Existing server closed gracefully, ready for restart');
      process.exit(0);
    });
  }
});

然后创建index.js(主进程,负责管理子进程):

const { fork } = require('child_process');
let serverProcess;

// 启动/重启服务器的函数
function startServer() {
  serverProcess = fork('./server.js');

  // 监听子进程的消息
  serverProcess.on('message', (msg) => {
    if (msg.type === 'restart') {
      console.log('Received restart request, starting restart process...');
      // 通知子进程优雅关闭
      serverProcess.send({ type: 'shutdown' });
      // 子进程退出后启动新的服务进程
      serverProcess.on('exit', () => {
        startServer();
      });
    }
  });

  // 处理子进程意外崩溃的情况,自动重启
  serverProcess.on('exit', (code) => {
    if (code !== 0) {
      console.log(`Server process crashed with code ${code}, restarting automatically...`);
      startServer();
    }
  });
}

// 启动初始服务
startServer();

运行方式:执行node index.js,之后调用POST http://localhost:8000/restart就能触发服务器重启了。

开发环境简化版:借助Nodemon

如果只是开发环境用,不用搞复杂的父子进程,直接借助Nodemon的自动重启特性就行:

const express = require('express');
const app = express();

app.post('/restart', (req, res) => {
  res.json({ message: 'Server restarting...' });
  // 延迟100ms退出,确保响应能发送给客户端
  setTimeout(() => {
    process.exit(0);
  }, 100);
});

const port = process.env.PORT || 8000;
app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});

运行方式:用nodemon server.js启动服务,调用重启接口后,Nodemon会检测到进程退出,自动重启服务器。

Hapi 实现方案

思路和Express的父子进程方案一致,只是把服务代码换成Hapi的写法:

创建hapi-server.js

const Hapi = require('@hapi/hapi');

const init = async () => {
  const server = Hapi.server({
    port: process.env.PORT || 8000,
    host: 'localhost'
  });

  // 重启接口
  server.route({
    method: 'POST',
    path: '/restart',
    handler: (request, h) => {
      process.send({ type: 'restart' });
      return { message: 'Server restart initiated successfully' };
    }
  });

  // 测试路由
  server.route({
    method: 'GET',
    path: '/',
    handler: (request, h) => {
      return 'Hello from the Hapi server!';
    }
  });

  await server.start();
  console.log(`Server running at ${server.info.uri}`);

  // 监听主进程的关闭指令
  process.on('message', (msg) => {
    if (msg.type === 'shutdown') {
      server.stop({ timeout: 5000 }).then(() => {
        console.log('Hapi server closed gracefully');
        process.exit(0);
      });
    }
  });
};

process.on('unhandledRejection', (err) => {
  console.error(err);
  process.exit(1);
});

init();

主进程文件还是用之前的index.js,只需要把fork('./server.js')改成fork('./hapi-server.js')即可,运行方式和Express的父子进程方案一样。

注意事项

  • 优雅关闭很重要:一定要调用server.close()(Express)或server.stop()(Hapi),避免正在处理的请求被强制中断。
  • 延迟退出:在发送响应后延迟一点再退出进程,确保响应已经成功发送给客户端,不然可能出现客户端收不到响应的情况。
  • 生产环境建议:父子进程的方案更适合生产,Nodemon是开发工具,不建议在生产环境使用。

内容的提问来源于stack exchange,提问作者Oss

火山引擎 最新活动