nodejs学习笔记——关于PM2

前言

PM2是node进程管理工具,可以利用它来简化很多node应用管理的繁琐任务,如性能监控、自动重启、负载均衡等。
通常我们借助PM去实现node服务的持久化(一旦开始使用,您的应用将达到持久化,在崩溃和机器重启时自启动。)同时也便于流程、日志的管理。


基本用法

安装好pm2之后,甚至不需要额外的参数配置,直接执行pm2 start app.js就可以后台运行我们的node服务。

--watch:监听应用目录的变化,自动重启热更新。
-i --instances:启用多少个实例,可用于负载均衡。
        如果-i 0或者-i max,则根据当前机器核数确定实例数目。
--ignore-watch:排除监听的目录/文件。比如忽略node_modules文件夹
-n --name:应用的名称。查看应用信息的时候可以用到。
-o --output :标准log路径。
-e --error :错误log路径。
--interpreter :解释器,通常用默认配置就好。
        例如node解释.js,python解释.py,需要调整则配置此项

其他常用功能:
list查看进程,stop停止进程,restart重启进程,delete删除进程,等字面意思就可以理解,较容易掌握。


cluster

如果大家用 PM2 (Process Manager 2) 管理 Node.js 进程,会发现它支持一种 cluster mode。开启 cluster mode 后,支持给 Node.js 创建多个进程。 如果将 cluster mode 下的 instances 设置为 max 的话,它还会根据服务器的 CPU 核心数,来创建对应数量的 Node 进程。

pm2 start app.js -i max只用添加 -i 选项开启

cluster模式的出现就是为了解决 Node.js 实例单线程运行,无法利用多核 CPU 的优势而出现的。工作进程借助child_process.fork()方法孵化出多个子进程,他们之间通过 IPC (内部进程通信)通道实现通信。
通过 fork() 复制的进程都是一个独立的进程,这个进程中有着独立的 V8 实例。 fork() 进程是昂贵的,Node 通过事件驱动的方式在单线程上解决了大并发的问题,启动多个进程只是为了充分将 CPU 资源利用起来,而不是为了解决并发问题
调用fork的进程为父进程,fork 出来的都是子进程。子进程和父进程具有相同的代码段、数据段、堆栈,但是它们的内存空间不共享。父进程(即 master 进程)负责监听端口,接收到新的请求后将其分发给下面的 worker 进程。这里涉及三个问题:父子进程通信、负载均衡策略以及多进程的端口监听。
备注:Linux 上 fork() 支持写时复制,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。因此子进程和父进程一开始是共享相同的内存空间。

nodejs学习笔记——关于PM2_第1张图片
cluster
  • cluster 用于监听 process(child) 子进程触发的各种事件
  • worker 在主进程中获取,用于和自身通信。当子进程触发事件时,会返回当前的 worker 以及相关的信息到主进程相应的事件中
  • process(parent) 主进程本身的进程实例,在通信过程中基本没有用到
  • process(child) 子进程本身的实例,只能在子进程获取用于监听自身的事件

可见主进程与子进程通过这样一个三角关系互相通信,其中 cluster 和 worker 是在主进程中获取的,process(child) 是子进程。 cluster 通过操作 worker 通知子进程,子进程本身和 cluster 进行通信。为什么要这样设计呢?因为子进程会有多个,只有通过 worker 才能选择和哪个进程通信。


负载均衡策略

前面提到,所有请求是通过 master 进程分配的,要保证服务器负载比较均衡的分配到各个 worker 进程上,这就涉及到负载均衡策略了。Node.js 默认采用的策略是 round-robin 时间片轮转法
round-robin 是一种很常见的负载均衡算法,Nginx 上也采用了它作为负载均衡策略之一。它的原理很简单,每一次把来自用户的请求轮流分配给各个进程,从 1 开始,直到 N(worker 进程个数),然后重新开始循环。这个算法的问题在于,它是假定各个进程或者说各个服务器的处理性能是一样的,但是如果请求处理间隔较长,就容易导致出现负载不均衡。因此我们通常在 Nginx 上采用另一种算法:WRR,加权轮转法。通过给各个服务器分配一定的权重,每次选出权重最大的,给其权重减 1,直到权重全部为 0 后,按照此时生成的序列轮询。
可以通过设置 NODE_CLUSTER_SCHED_POLICY 环境变量,或者通过 cluster.setupMaster(options) 来修改负载均衡策略。
所以我们不妨可以选择用 Nginx 做多机器集群上的负载均衡,然后用 Node.js Cluster 来实现单机多进程上的负载均衡。


cluster 的实现原理

当 worker 调用用 server.listen() 方法时会向 master 进程发送一个消息,让它创建一个服务器 socket ,做好监听并分享给该 worker 。如果 master 已经有监听好的 socket,就跳过创建和监听的过程,直接分享。换句话说,所有的 worker 监听的都是同一个 socket,当有新连接进来的时候,由负载均衡算法选出一个 worker 进行处理。

但是,在 Node.js 中,已经有 child_process 模块,让开发者得以开多个进程,实现每个进程各自利用一个 CPU,以实现多核的利用。
child_process 模块给予 Node.js 可以随意创建子进程的能力。因为 child_process 类本身是一个 EventEmitter,所以进程间通信很容易;且父子进程间通信并不通过网络层,而是在内核中完成,高效。

那为什么还需要 cluster 模块呢?岂不是多此一举吗?
child_process 对于开发者来说,编程模型还是过于复杂,需要操心的细节过多,比如:父进程崩溃了,子进程回收是需要开发者提供代码来处理的——如果开发者只是想单纯利用多核模型,对具体工作进程的控制粒度并没有太多设想,那这种开发模型无疑是令人头疼的。

针对这个问题,Node.js 提供了 cluster 模块。cluster 简化了父子模型编程模型,只区分当前进程是不是 Master,是 Master 就可以 fork 子进程,不是那就请行使 Worker 职责。至于什么资源的回收,负载的调配,uncaughtException 的处理,它自有安排。

所以本质上, Cluster 是 child_process 和 net 模块的组合应用。它不仅简化了编程模型,还使得共享监听同一端口成为可能。


参考

PM2官方文档
深入浅出 Node.js Cluster
Node-内置模块:集群 cluster

你可能感兴趣的:(nodejs学习笔记——关于PM2)