14-集群-cluster

77 阅读4分钟

集群-cluster

// 1. cluster模块概览
// node实例是单线程作业的,在服务端编程当中,通常会创建多个node实例来处理客户端的请求,以此来提升系统的吞吐量,对这样多个node实例我们称之为 cluster (集群)
// 借助node的cluster模块,开发着可以在几乎不修改原有项目代码的前提下,获取的集群服务带来的好处
// 集群有以下两个常见的实现方案,而node自带的cluster模块采用了方案二

// 1.1 方案一: 多个node实例+多个端口
// 集群内的node实例,各自监听不同的端口,再由反向代理实现请求到多个端口的分发
// 优点: 实现简单,各个实例相对独立,这对服务稳定性有一定的好处
// 缺点: 增加端口的占用,进程之间通信比较麻烦

// 1.2 方案二: 主进程向子进程转发请求
// 集群内,创建一个主进程(master),以及若干个子进程(worker),由master监听客户端请求,并根据特定的策略,转发给worker
// 优点: 通常只占用一个端口,通信相对简单,转发策略更加的灵活,
// 缺点: 实现相对复杂,对主进程的稳定性要求较高

入门实例

// 在cluster模块当中,主进程称为master,子进程称为worker
// 例子如下,创建与cpu数目相同的服务端数量,来处理客户端请求,注意,他们监听的都是相同的端口
const cpuNums = require("os").cpus().length;
const http = require("http");
if (cluster.isMaster) {
  for (let index = 0; index < cpuNums; index++) {
    cluster.fork();
  }
} else {
  http
    .createServer((req, res) => {
      res.end("response from workder", process.pid); // 当调用 http://127.0.0.1:3000/ 这里就可以返回当前是哪个进程来处理的
    })
    .listen(3000);
  console.log(`Worker ${process.pid} started`);
  // Worker 45572 started
  // Worker 50016 started
  // Worker 19352 started
  // Worker 18848 started
  // Worker 48940 started
  // Worker 52084 started
}

cluster 模块实现原理

// 了解 cluster模块,主要搞清楚三个问题
// 1. msater worker 如何通信
// 2. 多个 server 实例, 如何实现端口共享
// 3. 多个 server 实例,来自客户端的请求如何分发到多个 worker

问题一: master worker 如何通信

// 这个问题比较简单,master进程通过, cluster.fork() 来创建 worker 进程, cluster.fork()内部是通过 child_process.fork() 来创建子进程的
// 1. master 进程,worker进程是父子进程的关系
// 2. master进程, worker进程可以通过IPC进程进行通信

问题二: 如何实现端口共享

// 在前面的例子,多个worker中创建的server端口监听了同个端口3000,通常来说,多个进程监听同个端口系统会报错,那为什么我们的例子没问题呢?
// 在net模块当中,对listen()方法进行特殊处理,根据当前进程是master进程还是worker进程
// 1. master进程:在该端口上进行正常的监听请求(没做任何特殊处理)
// 2. worker进程: 创建server实例,然后通过IPC通道,向master进程发送信息,让master进程也创建server实例,并在该端口上监听请求,当请求进来的时候,master进程将请求转发给worker进程的server实例
// 归纳起来就是,master进程监听特定端口,并将客户端转发给worker进程

问题三:如何将请求分发到多个 worker

// 每当worker进程创建server实例来监听需求,都会通过IPC通道,在master上进行注册,当客户端请求到达,master 会负责将请求转发给对应worker
// 具体转发给哪个worker,这个由转发策略来决定,可以通过环境变量NODE_CLUSTER_POLICY设置,可以在 cluster.setupMaster(options)时候传入
// 默认的转发策略是轮巡(SCHED_RR)
// 当有客户端请求到达的时候,master会轮巡一遍worker列表,找到第一个空闲的worker,然后将该请求转发给这个worker

master 和 worker 内部通信的小技巧

// 在开发的过程当中,我们会通过 process.on('message',fn) 实现进程之间的通信
// 前面提到,master进程,worker进程在server实例的创建过程当中,也是通过IPC通道进行通信的,那会不会对我们开发的时候造成干扰,比如收到一堆其实不需要关心的信息
// 答案是不会的,那么是怎么做到的呢?
// 当发送的消息内包含 cmd 字段,且该字段以NODE_作为前缀,则该消息会被视为内部保留的信息,不过通过message事件抛出,但是可以通过监听 internalMessage 捕获
// 以worker进程通过master进程黄建server实例为例子,worker伪代码如下

const message = {
  cmd: "NODE_CLUSTER",
  act: "queryServer",
};
process.send(message);

worker.process.on("internalMessage", fn);

相关链接

官方文档:[https://nodejs.org/api/cluster.html](https://nodejs.org/api/cluster.html)
Node学习笔记:[https://github.com/chyingp/nodejs-learning-guide](https://github.com/chyingp/nodejs-learning-guide)