介绍
PM2 是一个 Node.js 守护进程管理器,可以用于管理和监控 Node.js 应用程序。它具有很多有用的功能,例如进程守护、负载均衡、自动重启、进程监控和性能统计等。PM2 可以在 Linux、macOS 和 Windows 等操作系统上运行,并且可以与各种 Web 服务器和数据库集成。
这里直接看文档吧 pm2 quick start
补充:
自动重启
PM2 可以在进程崩溃或异常退出时自动重启进程。您可以使用以下命令来启用自动重启:
css
pm2 start app.js --name myapp --autorestart
多环境部署
js
module.exports = {
apps : [{
name: "nodejs-column", // 启动进程名
script: "./app.js", // 启动文件
instances: 2, // 启动进程数
exec_mode: 'cluster', // 多进程多实例
env_development: {
NODE_ENV: "development",
watch: true, // 开发环境使用 true,其他必须设置为 false
},
env_testing: {
NODE_ENV: "testing",
watch: false, // 开发环境使用 true,其他必须设置为 false
},
env_production: {
NODE_ENV: "production",
watch: false, // 开发环境使用 true,其他必须设置为 false
},
log_date_format: 'YYYY-MM-DD HH:mm Z',
error_file: '~/data/err.log', // 错误日志文件,必须设置在项目外的目录,这里为了测试
out_file: '~/data/info.log', // 流水日志,包括 console.log 日志,必须设置在项目外的目录,这里为了测试
max_restarts: 10,
}]
}
在上面的配置中要特别注意 error_file 和 out_file,这里的日志目录在项目初始化时要创建好,如果不提前创建好会导致线上运行失败,特别是无权限创建目录时。其次如果存在环境差异的配置时,可以放置在不同的环境下,最终可以使用下面三种方式来启动项目,分别对应不同环境。
js
pm2 start pm2.config.js --env development
pm2 start pm2.config.js --env testing
pm2 start pm2.config.js --env production
PM2 源码浅浅的解析
- 首先我们需要寻找pm2项目的入口文件,先看一看它的package.json: 重点看
main和bin两个字段
入口文件就是main字段,也就是index.js,接下来查看index.js:

由上边知道, 我们找到了 lib/API.js
- 然后看
bin字段,我们找到bin/pm2->lib/binaries/CLI.js, 接下来我们去了解pm2 命令需要经过的流程
lib/binaries/CLI.js
js
var pm2 = new PM2();
看下 实例化 pm2 后干了啥,这里有一个技巧,就是代码行数太多的话,我们先点开代码结构看下,如下图所示, 
有个 pm2.connect() callback ,点进去
- 直接看官方注释:


看到这里,我们大致知道,处理命令行的内容,判断是否启动 守护进程, 什么意思先不管,我们看下 pm2 是从哪儿来的呢?回到代码最顶层
ini
var PM2 = require('../API.js');
我们到了 lib/API.js
lib/API.js
constructor中有一行代码尤其引人注意:
看来 pm2 启动的时候其实是 new 了一个 Client
然后我们先看下 API 下的属性和方法

然后继续向下,找到了 .start () 方法

这个方法一看重点就在于_startJson和_startScript,这一路上我们沿着 bin/pm2 ---> lib/binaries/CLI.js ---> API.js ---> start() ----> _startJson/_startScript 方法 -> startApps -> eachLimit -> that.Client.executeRemote('prepare') , 看不懂,没关系,先梳理功能
至于说 Client 是干什么, 我们按着 command 点击 new Client, 到了 lib/Client.js,
lib/Client.js
释义如下:

然后我们点开 client 这个对象,看下属性和方法

然后看下 start 方法,调用如下

然后顺着 Client.prototype.start ---> Client.prototype.pingDaemon ----> Client.prototype.launchDaemon ---> Client.prototype.launchRPC依次查看发现
Client 本身是创建所有 pm2 所需的文件和文件夹,后面的内容都是在 Client 原型上添加的一些方法,我们重点看一下Client.prototype.launchDaemon:
可以看到它就是利用 child_process 创建了一个 Daemon 子进程

还有一个重点就是在 launchRPC 中有

js
var req = axon.socket('req');
this.client = new rpc.Client(req);
也就是创建了一个rpcClient连接到了Daemon进程,那么这里的this.client就相当于Daemon实例,那么rpc是什么意思呢?rpc就是指调用另一个进程或者服务器上的函数,
然后我们回到前边的问题在lib/API.js->......-> _startJson -> startApps -> that.Client.executeRemote('prepare'):

在lib/Client.js -> Client.prototype.executeRemote 里看到是调用了 this.client:

我们会发现 executeRemote方法先判断 PM2 的守护进程 Daemon 是否启动
如果没有启动, this.start() -> Client.prototype.start -> ..... -> require('Daemon.js') -> new Daemon() 模块中的方法启动守护进程 RPC 服务,启动成功后再通知 Client 并建立 RPC 通信连接。
那么 Damon 又是啥呢?
lib/Daemon.js
lib/Daemon.js -> start -> innerStart() -> startLogic() -> God
innerStart

startLogic

God 又是啥?
lib/God.js
继续看 Main Daemon side file

God.prepare

God.executeApp

Cluster mode logic (for NodeJS apps) 在 lib/God/ClusterMode.js

fork mode logic 在 lib/God/ForkMode.js 自己看吧
顺带着也看下 lib/God/Reload.js 中的源码 softReload, 注释:
softReload will wait permission from process to exit

softReload 和 hardReload 的区别就是在 softCleanDeleteProcess 方法上

全局流程图

总结:上述涉及 多个 模块文件
CLI(lib/binaries/CLI.js处理命令行输入,如我们运行的命令:
js
pm2 start pm2.config.js --env development
API(lib/API.js)对外暴露的各种命令行调用方法,比如上面的 start 命令对应的 API->start 方法。Client (lib/Client.js)可以理解为命令行接收端,负责创建守护进程 Daemon,并与 Daemon(lib/Daemon.js)保持 RPC 连接。God (lib/God.js)主要负责进程的创建和管理,主要是通过 Daemon 调用,Client 所有调用都是通过 RPC 调用 Daemon,然后 Daemon 调用 God 中的方法。- 最终在 God 中调用 ClusterMode(
lib/God/ClusterMode.js)模块,在 ClusterMode 中调用 Node.js 的 cluster.fork 创建子进程。

-
如图中首先通过命令行解析调用
API,API中的方法基本上是与CLI中的命令行一一对应的,API 中的 start 方法会根据传入参数判断是否是调用的方法,一般情况下使用的都是一个 JSON 配置文件,因此调用 API 中的私有方法_startJson。 -
接下来就开始在
Client模块中流转了,在_startJson中会调用executeRemote方法,该方法会先判断 PM2 的守护进程 Daemon 是否启动,如果没有启动会先调用 Daemon 模块中的方法启动守护进程RPC 服务,启动成功后再通知Client并建立RPC 通信连接。 -
成功建立连接后,Client 会发送启动 Node.js 子进程的命令
prepare,该命令传递Daemon,Daemon中有一份对应的命令的执行方法,该命令最终会调用 God 中的prepare 进行主子进程的创建。 -
如果是
cluster_mode在 God 中会调用 God 文件夹下的ClusterMode模块,应用 Node.js 的cluster.fork创建子进程,这样就完成了整个启动过程。
综上所述,PM2 通过命令行,使用 RPC 建立 Client 与 Daemon 进程之间的通信,通过 RPC 通信方式,调用 God,从而应用 Node.js 的 cluster.fork 创建子进程的, 以上是启动的流程,对于其他命令指令,比如 stop、restart 等,也是一样的通信流转过程,你参照上面的流程分析就可以了
参考文章: