pm2学习笔记

背景

1. 进程管理

进程管理主要是指创建,终止和监控进程。进程管理器主要是用来确保你的应用在启动后能够保持在线。

2. pm2 是什么

pm2 是 nodejs 的进程管理器,默认支持负载均衡,能够守护进程。还支持查看应用运行时的性能,资源占用情况等。

3. 为什么要用 pm2

普通 node 进程的缺陷

  • 普通启动方式:node index.js,关闭终端就结束进程
  • node 进程挂掉和“僵死”:
    • 进程挂掉大概有以下几种原因:
      • 内存泄漏:内存一点点积累到达临界值爆掉
      • 死循环导致内存突增爆掉
      • 死循环导致磁盘写爆
      • 被动被kill,主动退出。
    • “僵死”大概有以下几种可能:
      • 有某个非常耗时的cpu操作正在执行
      • 写了个死循环(死循环不一定会导致服务挂掉,有可能只会cpu飙升,让服务处于假死状态)

不管发生上面哪种情况,都会造成服务的不可访问,需要等我们自己去发现问题后,然后重启应用。

pm2 启动的优势

  • 后台运行:pm2可以后台运行,终端关闭不影响。
  • 自动重启:在应用意外挂掉或者机器重启后,能够自动重启应用
  • 自动负载均衡:支持集群模式,可以方便的启动多个node进程,充分利用cpu和内存
  • 0 秒停机重启:集群模式下,可以达到重启时不停止服务
  • 可以监控应用性能,资源占用情况等,自动输出日志
  • 开发模式支持热更新

使用

1. 安装

pm2 本质是一个 cli ,可以通过 npm 进行安装

$ npm install pm2
# or
$ yarn add pm2

2. 启动

开发模式

开发 node 应用的时候,修改代码过后每次都要重启下应用,很麻烦。使用 pm2-dev 命令启动应用,应用会在在代码发生变更过后,自动重启。

$ pm2-dev start server.js

生产模式

  • pm2 start

    应用部署到生产环境后,可以使用 pm2 start 来启动应用

    $ pm2 start server.js
    
    ┌─────┬───────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
    │ id  │ name      │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
    ├─────┼───────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
    │ 0   │ server    │ default     │ 0.1.0   │ fork    │ 17471    │ 71s    │ 0    │ online    │ 0%       │ 77.5mb   │ ayo      │ disabled │
    └─────┴───────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
    

    该命令启动的应用,默认支持进程守护,并且可以通过 pm2 进行监控

  • 一些可以传给 cli 的常用配置

    • --name <app_name>

      指定应用名。不传的话默认为应用入口文件的文件名。如上图的 server

    • --watch

      在文件发生变化时自动重启。类似于开发模式的 pm2-dev start 命令

    • --max-memory-restart <200MB>

      该配置项设置后,应用会在运行内存达到设定值 200MB 后,自动重启。避免了 Node.js heap out of memory error 问题。

    • --log <log_path>

      指定日志输出目录

    • --restart-delay <delay in ms>

      设置自动重启的延迟时间

    • --cron <cron_pattern>

      定时任务。可以设置 一天中的固定重启时间一周内固定重启日期,或者 一个时间间隔(如 每 48 小时重启一次)cron_pattern 请参照这里

    • --no-autorestart

      关掉应用的自动重启

更多配置请参照 集成部署EcosystemFile

3. 管理进程

下面是部分常用命令行

$ pm2 restart app_name
$ pm2 reload app_name
$ pm2 stop app_name
$ pm2 delete app_name
  • app_name 的位置除了传应用名,还可以传:
    • all 针对所有进程进行操作
    • id 针对特定id的进程进行操作
  • 集群模式下,restart中断服务,而reload不会

详细介绍下以下命令:

查看应用列表 pm2 list

$ pm2 list

┌─────┬───────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id  │ name      │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼───────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0   │ app       │ default     │ 1.0.0   │ fork    │ 16573    │ 9m     │ 0    │ online    │ 0%       │ 57.3mb   │ ayo      │ disabled │
│ 2   │ index     │ default     │ 1.0.0   │ fork    │ 0        │ 0      │ 16   │ errored   │ 0%       │ 0b       │ ayo      │ disabled │
│ 1   │ server    │ default     │ 0.1.0   │ fork    │ 17471    │ 71s    │ 0    │ online    │ 0%       │ 77.5mb   │ ayo      │ disabled │
└─────┴───────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

运行上面的命令后,可以查看所有 pm2 启动的应用

  • id:应用 id
  • name:应用名称
  • namespace:在配置文件里面可以给应用设置 namespace。设置后可以使用命令行单独启动或停止某个 namespace 的应用。详细用法见 集成部署EcosystemFile
  • mode: 进程的运行方式。分为 forkcluster。详细介绍见 集群模式
  • pid:进程id
  • uptime:运行时间
  • :重启次数(从0开始计数)
  • status:进程是否在线
  • cpu:cpu占用率
  • mem:内存占用大小

查看应用详情信息 pm2 show app_name|app_id

$ pm2 show server
Describing process with id 1 - name server
┌───────────────────┬──────────────────────────────────────────────────┐
│ status            │ online                                           │
│ name              │ server                                           │
│ namespace         │ default                                          │
│ version           │ 0.1.0                                            │
│ restarts          │ 0                                                │
│ uptime            │ 60m                                              │
│ script path       │ /home/ayo/dev/demo/analytics-dashboard/server.js │
│ script args       │ N/A                                              │
│ error log path    │ /home/ayo/.pm2/logs/server-error.log             │
│ out log path      │ /home/ayo/.pm2/logs/server-out.log               │
│ pid path          │ /home/ayo/.pm2/pids/server-1.pid                 │
│ interpreter       │ node                                             │
│ interpreter args  │ N/A                                              │
│ script id         │ 1                                                │
│ exec cwd          │ /home/ayo/dev/demo/analytics-dashboard           │
│ exec mode         │ fork_mode                                        │
│ node.js version   │ 17.0.0                                           │
│ node env          │ N/A                                              │
│ watch & reload    │ ✘                                                │
│ unstable restarts │ 0                                                │
│ created at        │ 2021-12-03T08:33:01.489Z                         │
└───────────────────┴──────────────────────────────────────────────────┘

运行上面的命令后,可以查看应用详情信息。
除了 pm2 list 命令返回的信息以外,还可以获取几个常用信息:

  • script path:启动入口的文件路径
  • script args:启动文件的参数
  • error log path:错误日志的文件路径
  • out log path:输出日志的文件路径
  • exec mode:进程的模式
  • watch&reload:是否开启监听文件变动重启
  • unstable restarts:不稳定的重启次数

查看日志 pm2 logs

$ pm2 logs

C:\Users\fish2\.pm2\pm2.log last 15 lines:
PM2        | 2022-06-22T17:26:49: PM2 log: Concurrent actions   : 2
PM2        | 2022-06-22T17:26:49: PM2 log: SIGTERM timeout      : 1600
PM2        | 2022-06-22T17:26:49: PM2 log: ===============================================================================
PM2        | 2022-06-22T17:26:49: PM2 log: App [server:0] starting in -fork mode-
PM2        | 2022-06-22T17:26:49: PM2 log: App [server:0] online
PM2        | 2022-06-23T08:57:15: PM2 log: Stopping app:server id:0
PM2        | 2022-06-23T08:57:15: PM2 log: App [server:0] exited with code [1] via signal [SIGINT]
PM2        | 2022-06-23T08:57:15: PM2 log: pid=18708 msg=process killed
PM2        | 2022-06-23T09:35:21: PM2 log: App [server:0] starting in -fork mode-
PM2        | 2022-06-23T09:35:21: PM2 log: App [server:0] online
PM2        | 2022-06-23T09:59:15: PM2 log: Stopping app:server id:0
PM2        | 2022-06-23T09:59:15: PM2 log: App [server:0] exited with code [1] via signal [SIGINT]
PM2        | 2022-06-23T09:59:15: PM2 log: pid=7828 msg=process killed
PM2        | 2022-06-23T10:05:22: PM2 log: App [server:0] starting in -fork mode-
PM2        | 2022-06-23T10:05:22: PM2 log: App [server:0] online

C:\Users\fish2\.pm2\logs\server-error.log last 15 lines:
C:\Users\fish2\.pm2\logs\server-out.log last 15 lines:
0|server   |       [Symbol(kNeedDrain)]: false,
0|server   |       [Symbol(corked)]: 0,
0|server   |       [Symbol(kOutHeaders)]: [Object: null prototype]
0|server   |     },
0|server   |     _currentUrl: 'http:undefined/appmarket/api/client/types/',
0|server   |     [Symbol(kCapture)]: false
0|server   |   },
0|server   |   response: undefined,
0|server   |   isAxiosError: true,
0|server   |   toJSON: [Function (anonymous)]
0|server   | }
0|server   | ERR! [SSR] generate html template error
0|server   | [SSR] generating html content
0|server   | [SSR] generating html content
0|server   | [SSR] generating html content

还可以直接进入日志保存的目录查看。示例中日志目录为:C:\Users\fish2\.pm2\pm2.log

监控所有进程 pm2 monit

pm2 monit
  • 左上角 Process list:进程列表
  • 右上角 Logs:所有实时日志
  • 左下角 Custom Metrics:当前选中进程的指标
  • 右下角 Metadata:进程的信息

集成部署EcosystemFile

当我们需要管理多个应用,或者应用有多个运行环境时,使用 pm2 start 来启动应用+配置参数是很不方便的。所以,pm2 支持使用配置文件来启动和管理应用。

1. 生成配置文件

$ pm2 init simple

运行该命令可在工程下初始化一个 ecosystem.config.js 配置文件

module.exports = {
  apps : [{
    name   : "app1",
    script : "./app.js"
  }]
}
  • app是要管理应用的数组,每个对象都是一个应用的配置

  • 你也可以自己创建一个配置文件,文件名以 .config.js 结尾,pm2 会自动识别并读取该文件。

2. 配置项

基础配置

  • name:进程名
  • script:node 启动文件的路径
  • cwd :项目所在的目录
  • args :通过命令行传递给node启动文件的参数
  • interpreter :编译器的绝对路径(默认 node )
  • interpreter_args :传给编译器的参数
  • node_args:传给 node 的参数

进阶配置

  • instances :应用启动实例个数,仅在cluster模式有效
  • exec_mode :应用程序启动模式。可选 clusterfork,默认为 fork
  • watch :是否启用监控模式,默认是 false。如果设置成 true,当应用程序变动时,pm2会自动重载
  • ignore_watch :不用监听的文件,如:
    ignore_watch: [
       'node_modules',
       'logs',
    ]
    
  • max_memory_restart :应用运行内存达到设定值后,会自动重启。避免了 Node.js heap out of memory error 问题
  • env :应用中的默认环境变量
  • env_ :命令行中可传入的环境变量,覆盖默认环境变量
  • source_map_support :默认 true,支持 sourcemap 文件

日志配置

  • log_date_format :日志时间格式
  • error_file :错误日志存放路径
  • out_file :全部日志存放路径
  • combine_logs:是否将不同 id 的进程日志合并
  • merge_logs:同上

控制流

  • min_uptime:应用被视为已启动最小运行时间

    min uptime of the app to be considered started

  • max_restarts: 在应用状态被认定为 error 前,一定时间(min_uptime 指定的时间,若未配置,则为 1 秒)内的连续不稳定重启次数

    number of consecutive unstable restarts (less than 1sec interval or custom time via min_uptime) before your app is considered errored and stop being restarted

以上两个配置项一般需要同时设置。
示例:
server.js

setTimeout(function () {
    console.log('killed');
    process.exit(1)
}, 1000);

ecosystem.config.js

module.exports = {
  apps: [{
    name: "app-market",
    script: "./server.js",
    max_restarts: 4,
    min_uptime: 5000,
  }]
}

如示例所示,server 应用在启动 1s 后,会自己强行结束进程。这时, pm2 由于进程守护的特性,会去重新拉起应用。示例中设置了 min_uptime 为 5000(即 5s),max_restarts 为 4 次,所以 pm2 在进程停止(1s)后去重启进程,5s 钟可以内重启次数肯定可以达到 4 次。达到设定次数后,则停止重启应用。

max_restarts

当第 4 次重启时,应用状态将变为 error,pm2 将停止重启应用。上图重启次数显示为 3,是因为是从 0 开始计数的(0, 1, 2, 3 共 4 次)

  • listen_timeout:如果应用没有发送 ready 信号,间隔多长时间 reload。单位为毫秒。
  • wait_ready:是否等待进程发送 ready 信号。默认为 false。设置为 true 后,需要在应用内部添加 process.send('ready') 语句发送 ready 信号。

以上两个配置项一般需要搭配使用,详细使用场景见 优雅的启动与停止应用

  • kill_timeout:从告诉进程要关闭到强制关闭进程的间隔时间。

    module.exports = {
      apps : [{
        name: 'app',
        script: './app.js',
        kill_timeout: 3000
      }]
    }
    

    当 pm2 要停止或重启一个应用时,会按顺序给你的进程发送一系列系统信号。它首先会发送一个 SIGINT 信号告诉你应用将会被停止。然后 pm2 会等待 3s(示例中 kill_timeout 设置的时间)。若 3s 后应用没有自己停止(exit),pm2 会发送一个 SIGKILL 信号来强制停止应用。

    When a process is stopped/restarted by PM2, some system signals are sent to your process in a given order.
    First a SIGINT a signal is sent to your processes, signal you can catch to know that your process is going to be stopped. If your application does not exit by itself before 1.6s (customizable) it will receive a SIGKILL signal to force the process exit.

    详细使用场景见 优雅的启动与停止应用

  • shutdown_with_message:使用 process.send(‘shutdown’) 方式来代替 process.kill(pid, SIGINT) 停止应用。默认值为 false
    主要用于进程无法使用信号流来关闭的场景。

    When signals are not available your process gets killed. In that case you have to use --shutdown-with-message via CLI or shutdown_with_message in Ecosystem File and listen for shutdown events.

  • restart_delay:进程掉线后,等待多长时间自动重启, 默认 0s

  • autorestart: 是否开启自动重启。默认开启。

  • cron_restart: 定时重启。可以设置 一天中的固定重启时间一周内固定重启日期,或者 一个时间间隔(如 每 48 小时重启一次)cron_pattern 请参照这里

集群模式

1. pm2 的 cluster 模式与 fork 模式的区别

  • fork 模式
    fork 模式使用最基本的进程运行方式,只是单实例运行server,无法实现 TCP 连接共享。并且:

    我们知道 JavaScript 代码是运行在单线程上的,换句话说一个 Node.js 进程只能运行在一个 CPU 上。那么如果用 Node.js 来做 Web Server,就无法享受到多核运算的好处

  • cluster 模式
    集群模式允许 nodejs 应用程序在所有可用的 CPU 上进行扩展,而无需修改任何代码。可以显著提高应用程序的性能和可靠性,具体取决于可用的 CPU 数量。在底层,它使用了 Node.js 集群模块,这样扩展后的应用程序的子进程可以自动共享服务器端口。要了解更多信息,请参阅 Node.js 集群

    The cluster mode allows networked Node.js applications (http(s)/tcp/udp server) to be scaled across all CPUs available, without any code modifications. This greatly increases the performance and reliability of your applications, depending on the number of CPUs available. Under the hood, this uses the Node.js cluster module such that the scaled application’s child processes can automatically share server ports. To learn more, see How It Works in the official Node.js documentation on the cluster module.

2. 使用

  • 命令行启动
$ pm2 start app.js -i max

max 的意思是:把应用部署到服务器所有可用的CPU上,并运行尽可能多的进程

  • 通过配置文件启动

ecosystem.config.js

module.exports = {
  apps : [{
    script    : "app.js",
    instances : "max",
    exec_mode : "cluster"
  }]
}

注意:pm2 的负载均衡的特性是建立在以集群模式启动的基础上的,默认的启动模式是 fork,不支持负载均衡

3. 重载 Reload

集群模式下,由于 pm2 的 restart 命令会先杀掉所有进程,再重新拉起,会造成服务终止。但是 reload 命令可以实现 0s 重启,维护升级的时候不需要停机。
当重载时间过长或者无法重载时,pm2 会自动切换成 restart

4. 动态扩展

如果希望在运行中动态增加实例数,可以使用下面的命令:

$ pm2 scale <app_name> +4 # add 4 additional workers in realtime

Docker 集成

1. 在容器中安装 pm2

RUN npm install pm2 -g

2. 启动 pm2

  • 直接启动应用
CMD ["pm2-runtime", "app.js"]
  • 通过配置文件启动
CMD ["pm2-runtime", "ecosystem.config.js"]

注意:在容器中使用 pm2 启动应用时,要使用 pm2-runtime 而不是 pm2 start, 因为:

pm2 是默认后台启动的, docker 感知不到。CMD 命令执行完成,docker 容器就结束了。 所以直接使用 node app.js 启动应用后,应用能一直在容器中保持运行。 pm2 以后台形式运行,CMD 命令执行完成,docker 就认为可以退出了。

所以需要使用 pm2-runtime 来进行处理。pm2-runtime 是为 Docker 容器设计的,它将应用程序置于前台,从而使容器保持运行状态。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 概念 PM2(process manage)是JavaScript运行时Node.js的进程管理器。 基本操作 安...
    F_wind阅读 945评论 0 3
  • 前言 PM2是node进程管理工具,可以利用它来简化很多node应用管理的繁琐任务,如性能监控、自动重启、负载均衡...
    AmazRan阅读 1,294评论 0 1
  • 1、pm2的安装: npm install -g pm2@latest //@latest表示安装最新的版本 ...
    liuxuech阅读 835评论 0 0
  • 一、PM2是什么? PM2是 node.js项目 的进程管理工具,常用功能如下: 让 node.js项目 能在...
    LearnAnything阅读 289评论 0 0
  • 一、概述 1、pm2是什么?pm2是node进程管理工具。 2、为什么选它?与其他工具的对比 二、特性 1、后台运...
    我才是大田田阅读 32,154评论 0 18