在互联网后端服务场景下,我们会面对如何在进程挂掉的时候(例如爆CPU或者内存导致进程卡死),你的服务能不间断地提供服务,提供系统稳定性的问题。无论你的服务器是单机的还是跑在集群上,都需要思考在实际过程中如何做到这一点。而基于多任务操作系统的异步性特征(不可复现),我们需要回归到对服务进程进行管理和监控。
下面我讲一下怎么去做进程管理。
守护进程
脱离终端的控制,默默在操作系统背后跑的进程,就是守护进程(后台进程)。
不同操作系统用的技术是不一样的,Linux以前是init.d,后面转用Systemd去做进程管理,因为Linux有不同的发行分支,所以某些特殊的发行版会有一丢丢区别。
像nodejs还有pm2和forever等工具去做这类事情
其他还有如Supervisor
守护进程Wiki:https://zh.wikipedia.org/wiki/%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B
Linux守护进程的启动方法:https://www.ruanyifeng.com/blog/2016/02/linux-daemon.html
Systemd 入门教程:https://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html
中断信号
顺带要提一下中断信号,什么是中断信号呢?
摘抄下Wiki的关键名词描述,如下:
中断是用以提高计算机工作效率、增强计算机功能的一项重要技术。
最初引入硬件中断,只是出于性能上的考量。
后来被用于CPU外部与内部紧急事件的处理、机器故障的处理、时间控制等多个方面,并产生通过软件方式进入中断处理(软中断)的概念。
通俗点讲,就是先有硬件中断,然后后面引入了软中断,都是为了进程抢占计算资源去处理某些事件。
而信号(signal),则是一种软中断,信号机制是进程间通信的一种方式,采用异步通信方式。在终端,可通过kill -l
查看所有的signal信号。
中断信号WIKI:https://zh.wikipedia.org/wiki/%E4%B8%AD%E6%96%B7
Linux的中断信号:http://gityuan.com/2015/12/20/signal/
PM2
PM2是一个Nodejs写的进程管理工具。
我有看到其他一些PM2源码分析的文章写得很不错,也有助于我去看源码,但包版本不是最新的,也不会差很多,大同小异。我看的包版本是5.1.2
。
PM2 的功能、插件非常的丰富,但比较核心的功能其实不多:
- 多进程管理
- 系统信息监控
- 日志管理
PM2的执行方式(exec_mode
)有fork
和cluster
两种
- fork mode是通过
child_process
package的spawn
去开新进程 - cluster mode是通过
cluster
package的fork
去开子进程
nodejs中spawn
和fork
的区别是啥?
Spawn | Fork |
---|---|
通过命令行去开启新的进程 | 在原有进程上去开子进程 |
后面打算读完源码有时间再写一篇详细的文章再介绍
pm2源码分析:https://juejin.cn/post/6866081343454773262
pm2的信号流:https://pm2.keymetrics.io/docs/usage/signals-clean-restart/
代码控制优雅重启
Nodejs
Nodejs有cluster模块可以去创建多进程,pm2的cluster mode
也是基于cluster模块的。
一个最简单的实现优雅重启的例子
import * as cluster from 'cluster';
import { cpus } from 'os';
import * as process from 'process';
const numCPUs = cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork workers.
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
// 进程中断信号,这里就会有上面说的软中断信号
// 这里可以加入进程中断后应该重启进程或者其他操作,优雅重启的处理地方
if (signal) {
console.log(`worker was killed by signal: ${signal}`);
} else if (code !== 0) {
console.log(`worker exited with error code: ${code}`);
} else {
console.log(`worker ${worker.process.pid} died`);
}
});
} else {
// Workers can share any TCP connection
// 程序入口
// bootstrap()
console.log(`Worker ${process.pid} started`);
}
什么情况下会用到?例如,为了确保Http服务器能提供不间断的服务,防止一个进程短路导致服务器不可用,可以开多进程去控制重启出问题的进程,从而通过自修复达到服务不间断。
另外,我们在任何情况下,都应该监听进程的系统中断信号,做一下容错处理或者监控处理,一般进程如下。
process.on('exit', (code) => {
console.log('Process exit event with code: ', code);
});
像上面使用了cluster模块则是使用cluster.on('exit', (worker, code, signal) => {...})