PM2 / Node Cluster 笔记

NodeJS guokai 发表于 2 年前最后回复来自 qq2850071112 2 年前

Node在选型时即基于V8构建,所以它与浏览器的模型类似,是运行在单个进程的单个线程上,这样的好处是程序状态比较单一,没有锁和线程同步的问题,但如今CPU基本都是多核的,真正的服务器上CPU往往还是多个。由于一个Node进程只能利用一个核,所以Node 单线程的架构没法充分利用当前服务器的多核 CPU 性能,因此 Node.js 提供了原生的cluster模块,可以充分利用多核CPU的性能,同时可以增加程序的稳定性。

1. child_process模块

但面对单线程无法充分利用多核优势的情况,前人的经验是启动多进程即可,理想情况下每个进程各自利用一个CPU,以此实现多核CPU的利用。Node提供了child_process模块,可以通过child_process.fork()实现进程的复制。

var fork = require('child_process').fork;
var cpus = reuqire('os').cpus();

for(var i = 0; i < cpus.length; i++) {
    fork('./worker.js');
}

这就是著名的Mater-Worker主从模式,其中主进程不负责具体的业务处理,而是负责调度或者管理工作进程,工作进程负责具体的业务处理。通过fork()复制的进程都是一个独立的进程,在这个进程中有着独立而全新的V8实例,它需要至少30ms的启动时间和至少10MB的内存,所以fork()进程是昂贵的。
在主从模式中,主进程为了实现对子进程的管理和调度,还需要在进程间进行通信,即Inter-Process Communication (IPC)。进程间通信的目的是为了让不同的进程能够互相访问资源并进行协调工作。实现进程间通信的技术有很多,如命名管道、匿名管道、Socket、信号量、共享内存、消息队列、Domain Socket等。

2. Cluster模块

Node在v0.8中新增了cluster模块,在v0.8版本之前实现多进程架构必须通过child_process来实现,而现在通过cluster模块提供的较完善的API就可以解决多核CPU的利用率问题。事实上cluster模块就是child_process和net模块的组合应用。

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
  // Workers can share any TCP connection
  // In this case its a HTTP server
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}

3. Forever / PM2

  • Forever: A simple CLI tool for ensuring that a given node script runs continuously (i.e. forever)
  • PM2: Production process manager for Node.JS applications. Perfectly designed for microservice architecture.
安装和启动:
  • 安装 npm install pm2@latest -g
  • 控制 pm2 start app.js --name hotel-fe -i max -- -n val
    --后的参数用于传递给执行脚本 -x 使用fork模式,默认是cluster模式 -e error.log -o output.log

  • pm2 delete id/all/app_name 删除指定名称的进程,如果是一个名称多进程,则一起删除

  • pm2 stop id/all/app_name 停止指定名称的进程,如果是一个名称多进程,则一起停止
  • pm2 restart id/all/app_name 会重新加载代码,因为需要杀掉原有进程,所以服务会中断
  • pm2 reload all/app_name 会算在服务重启的次数中,类似于平滑重启
  • pm2 gracefulReload all/app_name
  • pm2 dump, pm2 save 保存进程
  • pm2 kill 守护进程被干掉,但是执行任意pm2命令后,只有守护进程会起来
硬重启:
pm2 dump
pm2 resurrect

监控:

pm2 list [list|ls|l|status]
pm2 monit, pm2 m
pm2 desc id/app_name
pm2 web

日志:

pm2 logs [id/app_name]
pm2 flush 删除所有日志
pm2 reloadLogs 通过信号SIGUSR2让已经打开的日志窗口重启

更新:

sudo npm install pm2 -g

更新内存里的pm2:

pm2 updatePM2
Help:
Usage: pm2 [cmd] app
Commands:
start [options] <file|json|stdin>                          start and daemonize an app
deploy <file|environment>                                  deploy your json
startOrRestart <json>                                      start or restart JSON file
startOrReload <json>                                       start or gracefully reload JSON file
startOrGracefulReload <json>                               start or gracefully reload JSON file
stop [options] <id|name|all|json|stdin>                    stop a process (to start it again, do pm2 restart <app>)
restart [options] <id|name|all|json|stdin>                 restart a process
reload <name|all>                                          reload processes (note that its for app using HTTP/HTTPS)
gracefulReload <name|all>                                  gracefully reload a process. Send a "shutdown" message to close all connections.
delete <name|id|script|all|json|stdin>                     stop and delete a process from pm2 process list
sendSignal <signal> <pm2_id|name>                          send a system signal to the target process
ping                                                       ping pm2 daemon - if not up it will launch it
updatePM2                                                  update in-memory PM2 with local PM2
update                                                     (alias) update in-memory PM2 with local PM2
interact [secret_key|command] [public_key] [machine_name]  agent actions for keymetrics.io - command can be stop|info|delete|restart web launch an health API on port 9615 dump dump all processes for resurrecting them later save (alias) dump all processes for resurrecting them later resurrect resurrect previously dumped processes startup <platform> auto resurrect process at startup. [platform] = ubuntu, centos, gentoo or systemd generate generate an ecosystem.json configuration file ecosystem generate an ecosystem.json configuration file reset <name|id|all> reset counters for process describe <id> describe all parameters of a process id desc <id> (alias) describe all parameters of a process id info <id> (alias) describe all parameters of a process id show <id> (alias) describe all parameters of a process id list list all processes ls (alias) list all processes l (alias) list all processes status (alias) list all processes jlist list all processes in JSON format prettylist print json in a prettified JSON monit launch termcaps monitoring m (alias) launch termcaps monitoring flush flush logs reloadLogs reload all logs logs [options] [id|name] stream logs file. Default stream all logs ilogs advanced interface to display logs kill kill daemon pull <name> [commit_id] updates repository for a given app forward <name> updates repository to the next commit for a given app backward <name> downgrades repository to the previous commit for a given app * undefined
Options:
-h, --help                           output usage information
-V, --version                        output the version number
-v --version                         get version
-s --silent                          hide all messages
-m --mini-list                       display a compacted list without formatting
-f --force                           force actions
-n --name <name>                     set a <name> for script
-i --instances <number>              launch [number] instances (for networked app)(load balanced)
-l --log [path]                      specify entire log file (error and out are both included)
-o --output <path>                   specify out log file
-e --error <path>                    specify error log file
-p --pid <pid>                       specify pid file
--max-memory-restart <memory>        specify max memory amount used to autorestart (in megaoctets)
--env <environment_name>             specify environment to get specific env variables (for JSON declaration)
-x --execute-command                 execute a program using fork system
-u --user <username>                 define user when generating startup script
-c --cron <cron_pattern>             restart a running process based on a cron pattern
-w --write                           write configuration in local folder
--interpreter <interpreter>          the interpreter pm2 should use for executing app (bash, python...)
--log-date-format <momentjs format>  add custom prefix timestamp to logs
--no-daemon                          run pm2 daemon in the foreground if it doesn't exist already
--merge-logs                         merge logs from different instances but keep error and out separated
--watch                              watch application folder for changes
--ignore-watch <folders|files>       folder/files to be ignored watching, chould be a specific name or regex - e.g. --ignore-watch="test node_modules "some scripts""
--node-args <node_args>              space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"
--no-color                           skip colors

Basic Examples:
Start an app using all CPUs available + set a name :
$ pm2 start app.js -i max --name "api"

Restart the previous app launched, by name :
$ pm2 restart api

Stop the app :
$ pm2 stop api

Restart the app that is stopped :
$ pm2 restart api

Remove the app from the process list :
$ pm2 delete api

Kill daemon pm2 :
$ pm2 kill

Update pm2 :
$ npm install pm2@latest -g ; pm2 updatePM2

More examples in https://github.com/Unitech/pm2#usagefeatures
Deployment help:
$ pm2 deploy help
暂无回复,说出你的观点吧
登录后即可参与回复