关于多线程:在nodejs中创建cluster

5次阅读

共计 4463 个字符,预计需要花费 12 分钟才能阅读完成。

简介

在后面的文章中,咱们讲到了能够通过 worker_threads 来创立新的线程,能够应用 child_process 来创立新的子过程。本文将会介绍如何创立 nodejs 的集群 cluster。

cluster 集群

咱们晓得,nodejs 的 event loop 或者说事件响应处理器是单线程的,然而当初的 CPU 基本上都是多核的,为了充分利用古代 CPU 多核的个性,咱们能够创立 cluster,从而使多个子过程来共享同一个服务器端口。

也就是说,通过 cluster,咱们能够应用多个子过程来服务解决同一个端口的申请。

先看一个简略的 http server 中应用 cluster 的例子:

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

if (cluster.isMaster) {console.log(` 主过程 ${process.pid} 正在运行 `);

  // 衍生工作过程。for (let i = 0; i < numCPUs; i++) {cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {console.log(` 工作过程 ${worker.process.pid} 已退出 `);
  });
} else {
  // 工作过程能够共享任何 TCP 连贯。// 在本例子中,共享的是 HTTP 服务器。http.createServer((req, res) => {res.writeHead(200);
    res.end('你好世界 \n');
  }).listen(8000);

  console.log(` 工作过程 ${process.pid} 已启动 `);
}

cluster 详解

cluster 模块源自于 lib/cluster.js,咱们能够通过 cluster.fork() 来创立子工作过程,用来解决主过程的申请。

cluster 中的 event

cluster 继承自 events.EventEmitter,所以 cluster 能够发送和接管 event。

cluster 反对 7 中 event,别离是 disconnect,exit,fork,listening,message,online 和 setup。

在解说 disconnect 之前,咱们先介绍一个概念叫做 IPC,IPC 的全称是 Inter-Process Communication,也就是过程间通信。

IPC 次要用来进行主过程和子过程之间的通信。一个工作过程在创立后会主动连贯到它的主过程。当 ‘disconnect’ 事件被触发时才会断开连接。

触发 disconnect 事件的起因有很多,能够是被动调用 worker.disconnect(),也能够是工作过程退出或者被 kill 掉。

cluster.on('disconnect', (worker) => {console.log(` 工作过程 #${worker.id} 已断开连接 `);
});

exit 事件会在任何一个工作过程敞开的时候触发。个别用来监测 cluster 中某一个过程是否异样退出,如果退出的话应用 cluster.fork 创立新的过程,以保障有足够多的过程来解决申请。

cluster.on('exit', (worker, code, signal) => {console.log('工作过程 %d 敞开 (%s). 重启中...',
              worker.process.pid, signal || code);
  cluster.fork();});

fork 事件会在调用 cluster.fork 办法的时候被触发。

const timeouts = [];
function errorMsg() {console.error('连贯出错');
}

cluster.on('fork', (worker) => {timeouts[worker.id] = setTimeout(errorMsg, 2000);
});

主过程和工作过程的 listening 事件都会在工作过程调用 listen 办法的时候触发。

cluster.on('listening', (worker, address) => {
  console.log(` 工作过程已连贯到 ${address.address}:${address.port}`);
});

其中 worker 代表的是工作线程,而 address 中蕴含三个属性:address、port 和 addressType。其中 addressType 有四个可选值:

  • 4 (TCPv4)
  • 6 (TCPv6)
  • -1 (Unix 域 socket)
  • ‘udp4’ or ‘udp6’ (UDP v4 或 v6)

message 事件会在主过程收到子过程发送的音讯时候触发。

当主过程生成工作过程时会触发 fork,当工作过程运行时会触发 online。

setupMaster 办法被调用的时候,会触发 setup 事件。

cluster 中的办法

cluster 中三个办法,别离是 disconnect,fork 和 setupMaster。

cluster.disconnect([callback])

调用 cluster 的 disconnect 办法,实际上会在 cluster 中的每个 worker 中调用 disconnect 办法。从而断开 worker 和主过程的连贯。

当所有的 worker 都断开连接之后,会执行 callback。

cluster.fork([env])

fork 办法,会从主过程中创立新的子过程。其中 env 是要增加到过程环境变量的键值对。

fork 将会返回一个 cluster.Worker 对象,代表工作过程。

最初一个办法是 setupMaster:

cluster.setupMaster([settings])

默认状况下,cluster 通过 fork 办法来创立子过程,然而咱们能够通过 setupMaster 来扭转这个行为。通过设置 settings 变量,咱们能够扭转前面 fork 子过程的行为。

咱们看一个 setupMaster 的例子:

const cluster = require('cluster');
cluster.setupMaster({
  exec: 'worker.js',
  args: ['--use', 'https'],
  silent: true
});
cluster.fork(); // https 工作过程
cluster.setupMaster({
  exec: 'worker.js',
  args: ['--use', 'http']
});
cluster.fork(); // http 工作过程 

cluster 中的属性

通过 cluster 对象,咱们能够通过 isMaster 和 isWorker 来判断过程是否主过程。

能够通过 worker 来获取当前工作过程对象的援用:

const cluster = require('cluster');

if (cluster.isMaster) {console.log('这是主过程');
  cluster.fork();
  cluster.fork();} else if (cluster.isWorker) {console.log(` 这是工作过程 #${cluster.worker.id}`);
}

能够通过 workers 来遍历沉闷的工作过程对象:

// 遍历所有工作过程。function eachWorker(callback) {for (const id in cluster.workers) {callback(cluster.workers[id]);
  }
}
eachWorker((worker) => {worker.send('告诉所有工作过程');
});

每个 worker 都有一个 id 编号,用来定位该 worker。

cluster 中的 worker

worker 类中蕴含了对于工作过程的所有的公共的信息和办法。cluster.fork 进去的就是 worker 对象。

worker 的事件和 cluster 的很相似,反对 6 个事件:disconnect,error,exit,listening,message 和 online。

worker 中蕴含 3 个属性,别离是:id,process 和 exitedAfterDisconnect。

其中 id 是 worker 的惟一标记。

worker 中的 process,实际上是 ChildProcess 对象,是通过 child_process.fork() 来创立进去的。

因为在 worker 中,process 属于全局变量,所以咱们能够间接在 worker 中应用 process 来进行发送音讯。

exitedAfterDisconnect 示意如果工作过程因为 .kill() 或 .disconnect() 而退出的话,值就是 true。如果是以其余形式退出的话,返回值就是 false。如果工作过程尚未退出,则为 undefined。

咱们能够通过 worker.exitedAfterDisconnect 来辨别是被动退出还是被动退出,主过程能够依据这个值决定是否从新生成工作过程。

cluster.on('exit', (worker, code, signal) => {if (worker.exitedAfterDisconnect === true) {console.log('这是自发退出,无需放心');
  }
});

// 杀死工作过程。worker.kill();

worker 还反对 6 个办法,别离是:send,kill,destroy,disconnect,isConnected,isDead。

这里咱们次要解说一下 send 办法来发送音讯:

worker.send(message[, sendHandle[, options]][, callback])

能够看到 send 办法和 child_process 中的 send 办法参数其实是很相似的。而实质上,worker.send 在主过程中,这会发送音讯给特定的工作过程。相当于 ChildProcess.send()。在工作过程中,这会发送音讯给主过程。相当于 process.send()。

if (cluster.isMaster) {const worker = cluster.fork();
  worker.send('你好');

} else if (cluster.isWorker) {process.on('message', (msg) => {process.send(msg);
  });
}

在下面的例子中,如果是在主过程中,那么能够应用 worker.send 来发送音讯。而在子过程中,则能够应用 worker 中的全局变量 process 来发送音讯。

总结

应用 cluster 能够充沛应用多核 CPU 的劣势,心愿大家在理论的我的项目中利用起来。

本文作者:flydean 程序那些事

本文链接:http://www.flydean.com/nodejs-cluster/

本文起源:flydean 的博客

欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

正文完
 0