介绍
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 等,也是一样的通信流转过程,你参照上面的流程分析就可以了
参考文章: