pm2 源码浅析

介绍

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_fileout_file,这里的日志目录在项目初始化时要创建好,如果不提前创建好会导致线上运行失败,特别是无权限创建目录时。其次如果存在环境差异的配置时,可以放置在不同的环境下,最终可以使用下面三种方式来启动项目,分别对应不同环境。

js 复制代码
pm2 start pm2.config.js --env development

pm2 start pm2.config.js --env testing

pm2 start pm2.config.js --env production

PM2 源码浅浅的解析

  1. 首先我们需要寻找pm2项目的入口文件,先看一看它的package.json: 重点看 mainbin 两个字段

入口文件就是main字段,也就是index.js,接下来查看index.js:

由上边知道, 我们找到了 lib/API.js

  1. 然后看 bin 字段,我们找到 bin/pm2 -> lib/binaries/CLI.js, 接下来我们去了解 pm2 命令需要经过的流程

lib/binaries/CLI.js

js 复制代码
var pm2 = new PM2();

看下 实例化 pm2 后干了啥,这里有一个技巧,就是代码行数太多的话,我们先点开代码结构看下,如下图所示,

有个 pm2.connect() callback ,点进去

  1. 直接看官方注释:

看到这里,我们大致知道,处理命令行的内容,判断是否启动 守护进程, 什么意思先不管,我们看下 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

softReloadhardReload 的区别就是在 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 创建子进程。
  1. 如图中首先通过命令行解析调用 APIAPI 中的方法基本上是与 CLI 中的命令行一一对应的,API 中的 start 方法会根据传入参数判断是否是调用的方法,一般情况下使用的都是一个 JSON 配置文件,因此调用 API 中的私有方法 _startJson

  2. 接下来就开始在 Client 模块中流转了,在 _startJson 中会调用 executeRemote 方法,该方法会先判断 PM2 的守护进程 Daemon 是否启动,如果没有启动会先调用 Daemon 模块中的方法启动守护进程 RPC 服务,启动成功后再通知 Client 并建立 RPC 通信连接

  3. 成功建立连接后,Client 会发送启动 Node.js 子进程的命令 prepare,该命令传递 DaemonDaemon 中有一份对应的命令的执行方法,该命令最终会调用 God 中的 prepare 进行主子进程的创建

  4. 如果是 cluster_mode 在 God 中会调用 God 文件夹下的 ClusterMode 模块,应用 Node.js 的 cluster.fork 创建子进程,这样就完成了整个启动过程。

综上所述,PM2 通过命令行,使用 RPC 建立 Client 与 Daemon 进程之间的通信,通过 RPC 通信方式,调用 God,从而应用 Node.js 的 cluster.fork 创建子进程的, 以上是启动的流程,对于其他命令指令,比如 stop、restart 等,也是一样的通信流转过程,你参照上面的流程分析就可以了

参考文章:

  1. 由浅入深带你玩透pm2
  2. PM2源码浅析
  3. pm2 详解
  4. pm2 文档
  5. cluster模式以及 pm2 工具的介绍
  6. Node的Cluster模块和PM2 的原理介绍
  7. PM2源码分析
相关推荐
午与羽1 个月前
Nestjs构建Certeasy证书自动化平台 - 系统部署
运维·docker·pm2
ysyxg3 个月前
Nvm和Npm和Pm2的关系和使用说明
前端·npm·node.js·pm2
Vinkan@6 个月前
nuxt3项目服务端bulid后在本地浏览的3种方式(nuxi preview、Node.js Server、PM2)
pm2·nuxt3·pm2.io
没有技巧全是感情7 个月前
解决node连接mysql长时间不操作数据库自动断开报错问题
mysql·pm2
怪力左手7 个月前
pm2进程监控
pm2·进程监控
原谅我不够洒脱9 个月前
windows pm2 执行 npm脚本或执行yarn脚本遇到的问题及解决方案
windows·npm·pm2
乐闻x1 年前
使用PM2,在生产环境稳定运行你的node项目
npm·node.js·pm2
肖无疾1 年前
Windows下pm2调用npm和nuxt的办法
前端·npm·node.js·nuxt·pm2
CoderLiu1 年前
看了pm2的源码后,发现了集群模式下无法切换node版本的奥秘
前端·前端框架·pm2