使用PM2的GracefulReload无停机更新Express应用
Yourtion 创作于:2017-08-29
全文约 2076 字,
预计阅读时间为 6 分钟
最近的项目上遇到一个问题,在 API 服务中,有些请求是先返回了结果,然后在后面继续处理一些异步操作,但是如果这时候重启了服务,因为部分操作没执行成功,就会导致数据不一致的情况。
很早之前就知道了 PM2 的 GracefulReload,而且在实际项目中也有使用,但是基本都是以连接断开为标记,这次就顺便研究了一下怎么样更优雅的实现无停机更新。
最简单的版本
const http = require('http');
const express = require('express');
...
const server = http.createServer(app);
server.listen(3000, () => {
console.log('Express is listening on 3000');
});
process.on('SIGINT', () => {
console.log('Closing server...');
server.close(() => {
console.log('Server closed !!! ');
process.exit();
});
});
简单的说就是跟 PM2 的官网说的一样,接收 SIGINT
参数,然后通过执行 server.close
方法,不再接收新的连接,并等待旧的连接响应完成才回调回来。
这样的版本并不能解决在回调后还有异步处理的问题。所以就有了下面的扩展版。
扩展升级版本
const http = require('http');
const express = require('express');
...
const server = http.createServer(app);
server.listen(3000, () => {
console.log('Express is listening on 3000');
});
process.on('SIGINT', () => {
const cleanUp = () => {
mysql.end(console.error);
redis.end();
};
server.close(() => {
// Stop after 10 secs
setTimeout(() => {
cleanUp();
process.exit();
}, 10000);
});
// Force close server after 15 secs
setTimeout((e) => {
console.log('Forcing server close !!!', e);
cleanUp();
process.exit(1);
}, 15000);
});
这个版本添加了对 mysql
和 redis
资源的释放,同时在连接断开后等待 10 秒,让异步的请求执行完(requert
上的超时为 5s,所有 10s 应该是足够了)。
效果测试
在启动的时候加上 kill-timeout
参数,或者在 json 文件中中设置,同时注意使用 cluster
模式启动。
pm2 start app.js --kill-timeout 15000
经过测试,这样的配置运行良好,可以从下面的日志看出来:
PM2 | Starting execution sequence in -cluster mode- for app name:Express id:1
PM2 | App name:Express id:1 online
1| Express | 08-29 10:44:51: Express is listening on 3000
PM2 | -softReload- New worker listening
PM2 | Stopping app:Express id:_old_1
PM2 | App name:Express id:_old_1 disconnected
PM2 | App [Express] with id [_old_1] and pid [24262], exited with code [0] via signal [SIGINT]
PM2 | pid=24262 msg=process killed
可以看出,在执行了 startOrGracefulReload
之后,PM2 会马上启动一个新的进程处理新进的请求,同时等待原有的进程停止后删除退出。至此,应该算是大功告成了。
参考
原文链接:https://blog.yourtion.com/graceful-reload-express-with-pm2.html