1 Star 4 Fork 1

dony / 学习笔记

Create your Gitee Account
Explore and code with more than 8 million developers,Free private repositories !:)
Sign up
This repository doesn't specify license. Please pay attention to the specific project description and its upstream code dependency when using it.
Clone or Download
Node.js的任务执行器.md 22.22 KB
Copy Edit Web IDE Raw Blame History
dony authored 2022-01-23 11:16 . update Node.js的任务执行器.md.

PM2---Node.js的任务执行器

PM2是一款可以为服务器实例带来负载均衡功能的生产级别的进程管理器,通过PM2,我们可以自由伸缩Node.js应用。此外,它能确保进程持续运行,解决Node.js单线程模型带来的副作用:一个没有被捕获的异常通过杀死进程,进而杀死整个应用

单线程应用及异常

前面提过,Node.js应用是单线程执行的,这不表示Node.js不能并发,它表示你的应用是以单线程模式运行的,而其余任务是并行的。

单线程模式意味着,如果抛出的异常没有被处理的话,应用程序就会挂掉

这个问题可以通过使用Promise库(例如bluebird)解决,通过Promise方式,应用不仅可以处理成功的返回,还能够处理异常,因此它可以防止异常“冒泡”导致应用奔溃。

然而,还存在一些在我们控制范围之外的情况,我们称之为不可恢复的错误。一旦出现这些错误,最终将导致你的应用程序奔溃。在诸如Java的语言中,异常并不是什么大问题,这些异常虽然导致线程的死亡,但是应用仍然会继续工作。

在Node.js中,这却是一个大问题。但是,我们可以通过任务执行器,例如forever, 来解决这个问题。

forever与PM2都是任务执行器,当你的应用意外退出时,它们可以重启你的应用,从而确保其正常运行。

dony@donydeMBP test % forever helloWorld.js 
warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
Server running at http://127.0.0.1:8000

当helloWorld.js被forever控制时,forever会在helloWorld应用挂掉之后,重启应用。如果我们杀死helloWorld应用,先查看forever占用的进程ID

dony@donydeMBP test % forever list
(node:1980) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:1980) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
info:    Forever processes running
data:        uid  command             script        forever pid  id logfile                       uptime                   
data:    [0] Civ0 /usr/local/bin/node helloWorld.js 1872    1873    /Users/dony/.forever/Civ0.log 0:0:6:36.293000000000006 

如你所见,forever生成另外一个PID为1873的进程,现在,通过kill命令杀死该进程,输出如下结果

dony@donydeMBP test % kill 1873
error: Forever detected script was killed by signal: SIGTERM
error: Script restart attempt #1
Server running at http://127.0.0.1:8000

虽然我们杀死了该进程,但是forever会立刻重启应用,几乎不会出现可感知的停机时间。并且,无论你杀死应用多少次,它都会将其重启。

在Node.js的生态圈中,还有一个相当有用的工具包nodemon,当它探测到监控的文件(默认监控工程下所有文件,即“.”)发生变化时,它将重载应用

dony@donydeMBP test % nodemon helloWorld.js 
[nodemon] 2.0.11
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node helloWorld.js`
Server running at http://127.0.0.1:8000

如上图所示,如果修改了helloWorld.js文件,nodemon会重载应用,这可以避免有编辑、重载周期带来的开销。例如当我们写完一段代码后,想立即看效果,直接保存下文件,便会重载应用,大大提升了开发效率

PM2———业界标准的任务执行器

fork模式

虽然forever已经相当不错了,但是PM2比forever更胜一筹。通过PM2,你可以管理应用的整个生命周期,并且实现没有停机时间。只要通过简单的命令就可以伸缩应用。

PM2也具备负载均衡的功能。

这里以一个简单的http服务器为例

var http = require('http');
var server = http.createServer((request, response) => {
	console.log('called');
	response.writeHead(200, {"Content-Type": "text/plain"});
	response.end("Hello World\n");
});
server.listen(8000);
console.log("Server running at http://127.0.0.1:8000");

这是一个相当简单的应用,我们通过PM2来运行它。

pm2 start helloWorld.js
[PM2] Applying action restartProcessId on app [helloWorld](ids: [ 0 ])
[PM2] [helloWorld](0)
[PM2] Process successfully started
┌─────┬───────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
id  │ name          │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼───────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0   │ helloWorld    │ default     │ N/A     │ fork    │ 2538     │ 0s     │ 0    │ online    │ 1%       │ 1.4mb    │ dony     │ disabled │
└─────┴───────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

PM2已经注册了一个名为helloWorld的应用。这个应用运行在fork模式下,该模式下PM2未进行负载均衡处理,而是简单的fork该应用,本例中该应用的PID为2538.

现在输入命令pm2 show 0,将得到id为0的应用的相关信息,如下图所示:

dony@donydeMBP test % pm2 show 0
 Describing process with id 0 - name helloWorld 
┌───────────────────┬────────────────────────────────────────────────┐
│ status            │ online                                         │
│ name              │ helloWorld                                     │
│ namespace         │ default                                        │
│ version           │ N/A                                            │
│ restarts          │ 0                                              │
uptime            │ 3m                                             │
│ script path       │ /Users/dony/projects/nodejs/test/helloWorld.js │
│ script args       │ N/A                                            │
│ error log path    │ /Users/dony/.pm2/logs/helloWorld-error.log     │
│ out log path      │ /Users/dony/.pm2/logs/helloWorld-out.log       │
│ pid path          │ /Users/dony/.pm2/pids/helloWorld-0.pid         │
│ interpreter       │ node                                           │
│ interpreter args  │ N/A                                            │
│ script id         │ 0                                              │
exec cwd          │ /Users/dony/projects/nodejs/test               │
exec mode         │ fork_mode                                      │
│ node.js version   │ 14.18.1                                        │
│ node env          │ N/A                                            │
│ watch & reload    │ ✘                                              │
│ unstable restarts │ 0                                              │
│ created at        │ 2022-01-23T04:33:29.996Z                       │
└───────────────────┴────────────────────────────────────────────────┘
 Actions available 
┌────────────────────────┐
│ km:heapdump            │
│ km:cpu:profiling:start │
│ km:cpu:profiling:stop  │
│ km:heap:sampling:start │
│ km:heap:sampling:stop  │
└────────────────────────┘
 Trigger via: pm2 trigger helloWorld <action_name>

 Code metrics value 
┌────────────────────────┬──────────┐
│ Heap Size              │ 6.32 MiB │
│ Heap Usage             │ 76.04 %  │
│ Used Heap Size         │ 4.81 MiB │
│ Active requests        │ 0        │
│ Active handles         │ 4        │
│ Event Loop Latency     │ 0.55 ms  │
│ Event Loop Latency p95 │ 1.66 ms  │
└────────────────────────┴──────────┘
 Divergent env variables from local env 

通过这两个命令,我们已经可以相当全面地管理一个简单应用的执行情况。

从现在开始,PM2能够确保你的应用一直处于运行状态,如果你的应用挂掉了,PM2将对其进行重启。

我们也可以监控PM2下运行的应用列表:

pm2 monit

将得到如下输出:

┌─ Process List ───────┐┌──  helloWorld Logs  ─────────────────────────────────┐
│[ 0] helloWorld       ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
│                      ││                                                      │
└──────────────────────┘└──────────────────────────────────────────────────────┘
┌─ Custom Metrics ─────┐┌─ Metadata ───────────────────────────────────────────┐
│ Heap Size     6.32   ││ App Name              helloWorld                     │
│ Heap Usage           ││ Namespace             default                        │
│ Used Heap Size       ││ Version               N/A                            │
│ Active requests      ││ Restarts              0                              │
└──────────────────────┘└──────────────────────────────────────────────────────┘
 left/right: switch boards | up/down/mouse: scroll | Ctrl-C: exit         To go

上图是PM2监控显示结果。在本例中,我们系统只有一个运行在fork模式下的应用,所以显得又些大材小用。

通过pm2 logs命令,可以查看输出日志,如下图所示

dony@donydeMBP test % pm2 logs
[TAILING] Tailing last 15 lines for [all] processes (change the value with --lines option)
/Users/dony/.pm2/pm2.log last 15 lines:
PM2        | 2022-01-23T12:32:55: PM2 log: RPC socket file      : /Users/dony/.pm2/rpc.sock
PM2        | 2022-01-23T12:32:55: PM2 log: BUS socket file      : /Users/dony/.pm2/pub.sock
PM2        | 2022-01-23T12:32:55: PM2 log: Application log path : /Users/dony/.pm2/logs
PM2        | 2022-01-23T12:32:55: PM2 log: Worker Interval      : 30000
PM2        | 2022-01-23T12:32:55: PM2 log: Process dump file    : /Users/dony/.pm2/dump.pm2
PM2        | 2022-01-23T12:32:55: PM2 log: Concurrent actions   : 2
PM2        | 2022-01-23T12:32:55: PM2 log: SIGTERM timeout      : 1600
PM2        | 2022-01-23T12:32:55: PM2 log: ===============================================================================
PM2        | 2022-01-23T12:32:55: PM2 log: App [helloWorld:0] starting in -fork mode-
PM2        | 2022-01-23T12:32:55: PM2 log: App [helloWorld:0] online
PM2        | 2022-01-23T12:33:25: PM2 log: Stopping app:helloWorld id:0
PM2        | 2022-01-23T12:33:25: PM2 log: App [helloWorld:0] exited with code [0] via signal [SIGINT]
PM2        | 2022-01-23T12:33:25: PM2 log: pid=2519 msg=process killed
PM2        | 2022-01-23T12:33:29: PM2 log: App [helloWorld:0] starting in -fork mode-
PM2        | 2022-01-23T12:33:30: PM2 log: App [helloWorld:0] online

/Users/dony/.pm2/logs/helloWorld-error.log last 15 lines:
/Users/dony/.pm2/logs/helloWorld-out.log last 15 lines:
0|helloWor | Server running at http://127.0.0.1:8000
0|helloWor | Server running at http://127.0.0.1:8000

如你所见,PM2相当出色,只需要很少的命令,就能够覆盖90%的监控需求,然而,这还只是“冰山一角”。

PM2还可以让你轻易的无缝重启应用:

pm2 reload all

这个命令可以确保你的应用能够重启,并且无停机时间。PM2会将你的请求放入队列,等待重启后的应用再次响应请求。还有一种更加细密度的选择,可以通过加上应用名来制定需要重启的应用:

pm2 reload helloWorld	

集群模式

PM2将启动一个控制进程和指定个数的工作进程(你的应用)。因此,单线程模型的Node.js也能享受到多核CPU带来的性能提升。

在切换到这个模式之前,我们得先停止之前的应用,将得到如下所示的输出:

dony@donydeMBP test % pm2 stop all
[PM2] Applying action stopProcessId on app [all](ids: [ 0 ])
[PM2] [helloWorld](0)
┌─────┬───────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
id  │ name          │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼───────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0   │ helloWorld    │ default     │ N/A     │ fork    │ 0        │ 0      │ 0    │ stopped   │ 0%       │ 0b       │ dony     │ disabled │
└─────┴───────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

PM2会记住之前运行过的应用,因此,在重新使用集群模式运行应用之前,应该先让PM2抹去关于之前应用的记忆:

dony@donydeMBP test % pm2 delete all
[PM2] Applying action deleteProcessId on app [all](ids: [ 0 ])
[PM2] [helloWorld](0)
┌─────┬───────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
id  │ name      │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
└─────┴───────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

现在,我们使用集群模式启动应用:

dony@donydeMBP test % pm2 start helloWorld.js -i 3
[PM2] Starting /Users/dony/projects/nodejs/test/helloWorld.js in cluster_mode (3 instances)
[PM2] Done.
┌─────┬───────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
id  │ name          │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼───────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0   │ helloWorld    │ default     │ N/A     │ cluster │ 3372     │ 0s     │ 0    │ online    │ 0%       │ 35.0mb   │ dony     │ disabled │
│ 1   │ helloWorld    │ default     │ N/A     │ cluster │ 3373     │ 0s     │ 0    │ online    │ 0%       │ 30.5mb   │ dony     │ disabled │
│ 2   │ helloWorld    │ default     │ N/A     │ cluster │ 3375     │ 0s     │ 0    │ online    │ 0%       │ 20.7mb   │ dony     │ disabled │
└─────┴───────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

PM2充当控制主线程与3个工作进程之间的轮询调度器,因此它们可以并行的处理3个请求。我们能够自由的增加、减少工作进程:

pm2 scale helloWorld 2

通过执行上述命令,工作进程由3个减少为2个。

dony@donydeMBP test % pm2 scale helloWorld 2
[PM2] Applying action deleteProcessId on app [0](ids: [ 0 ])
[PM2] [helloWorld](0)
┌─────┬───────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
id  │ name          │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼───────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 1   │ helloWorld    │ default     │ N/A     │ cluster │ 3373     │ 3m     │ 0    │ online    │ 0%       │ 36.1mb   │ dony     │ disabled │
│ 2   │ helloWorld    │ default     │ N/A     │ cluster │ 3375     │ 3m     │ 0    │ online    │ 0%       │ 35.6mb   │ dony     │ disabled │
└─────┴───────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

如上图所示,我们能够毫不费力地配置应用,以做好生产准备。

现在,我们可以保存PM2的当前状态。这样一来,当重启服务器时,PM2将会始终以守护进程的形式运行,我们的应用可以自动重启。

Comment ( 0 )

Sign in to post a comment

1
https://gitee.com/dony1122/note.git
git@gitee.com:dony1122/note.git
dony1122
note
学习笔记
master

Search