关于 Node.js 中的多进程模型

多进程模型

Node.js 如何榨干服务器资源享受多核 CPU?

  • Node.js 是运行在单线程上的,因此也就表示 Node.js 只能运行在一个 CPU 上
  • 为了利用多核系统, Node.js 官方提供 🍭Cluster 模块 作为解决方案

Cluster 是什么?

  • 在服务器上同时启动多个进程
  • 每个进程里都跑的是同一份源代码
  • 这些进程可以同时监听一个端口

其中

  • 负责启动其他进程的模块叫做 Master 进程,Master 只负责启动(child_process.fork() 方法)其他进程,不负责具体工作
  • 其他被启动的进程称为 Worker 进程,负责接受请求,对外提供服务
  • Worker 数量一般由 CPU 核数确定,从而完美利用多核资源

Cluster 模块通过两种方式分发请求:

  1. 轮询(除 Windows 操作系统外的默认方法):主进程监听端口,接收新的连接请求,并采用轮询方式分发给 Worker 进程以实现负载均衡
  2. 主进程创建监听套接字,将请求直接转发给相应 Worker

理论上,第二种方法较好,然而在实践中,操作系统调度程序千差万别,导致负载更趋向于不平衡

Node.js 不提供路由逻辑,因此需要专门创建一个 application 来管理 Session 或 Login 等信息,以避免过分依赖内存数据

  • 由于 Worker 都是各自独立的进程,因此他们会在彼此互不影响的情况下,根据程序需要被 kill 或者重新创建
  • 只要还有 alive 的 Worker,服务器就会继续接收连接请求,如果没有 alive 的 Worker,现有的连接会被丢弃,同时也不再接收新的连接请求
  • 由于 Node.js 不会自动的管理 Worker 的数量,application 也需要根据自己的需求来管理 Worker 池

子进程

父子进程的相互影响

  • 当一个子进程退出时,并不立刻清空进程表,而是向父进程发送一个信号。父进程需要对此应答,然后系统会完全清除子进程。假设父进程没有应答,或者应答之前子进程退出,子进程会被系统设置为‘僵尸’状态
  • 当一个父进程退出时,如果有几个子进程仍在运行,这些子进程会变成‘孤儿进程’。‘孤儿进程’会立刻被‘init’超级进程接管,作为其父进程。‘init’进程能够确保这些子进程在退出时不会变为‘僵尸进程’,因为‘init’进程总是应答子进程的退出

POSIX 是什么?POSIX 的 fork()child_process.fork() 又何不同

  • POSIX 是线程的 POSIX 标准,该标准定义了创建和操作线程的一套 API
  • child_process.fork() 不会 clone 当前进程,而 POSIX 的 fork() 会复制父进程的‘内存页’

child_process.fork()child_process.spawn() 创建子进程的一种特殊方式,两者都返回新创建的子进程,但是通过 child_process.fork() 创建的子进程会默认开启进程间通信

POSIX 的进程间通信(IPC)

  • pipe():只能在父-子进程间使用;单向通信;传输字节流,读写顺序一致;不会丢失数据
  • mkfifo():命名管道,操作方式与操作文件类似
  • socket():与管道相似;进程间可以不想管;双向通信
  • mmap() 创建内存映射:映射同一个文件同一个区域,共享相同的‘物理内存页’;通过 fork() 创建的子进程,会复制父进程的‘内存页’,可以在这些内存页中映射共享内存
  • 消息队列
  • 信号量
  • 共享内存

Node.js 创建子进程的几种方式

  • spawn()
  • exec()
  • execFile()
  • fork()

Node.js 的进程守护

  • 鲁棒性(即健壮性)是 Node.js 所面临的一大问题,由于 Node.js 是单线程的,所以它最大的问题是不够稳定,不适合处理复杂业务
  • 捕获 Node.js 的异常可以有以下方式:

    1. process.on('uncaughtException', callback())
    2. try/catch
  • 进程守护可以采用插件(supervisor、forever、pm2 等)


ps:推荐使用进程管理器 pm2,带有负载均衡功能,配置简单,启动方便,秒速重载(小程序、钉钉企业微应用等需要签名验证功能请开启单核)

pps:一些常用的负载均衡算法:

  • 随机算法
  • 轮询及加权轮询
  • 最小连接及加权最小连接
  • 哈希算法
  • IP 地址散列
  • URL 散列