统计
  • 建站日期:2019-12-01
  • 文章总数:2331 篇
  • 评论总数:2160 条
  • 分类总数:22 个
  • 最后更新:4月1日
文章 未分类

在nodejs中创建cluster

程序员阿鑫
首页 未分类 正文


在nodejs中创建cluster
-程序员阿鑫-带你一起秃头
-第1
张图片

简介

在前面的文章中,我们讲到了可以通过worker_threads来创建新的线程,可以使用child_process来创建新的子进程。本文将会介绍如何创建nodejs的集群cluster。

cluster集群

我们知道,nodejs的event loop或者说事件响应处理器是单线程的,但是现在的CPU基本上都是多核的,为了充分利用现代CPU多核的特性,我们可以创建cluster,从而使多个子进程来共享同一个服务器端口。

也就是说,通过cluster,我们可以使用多个子进程来服务处理同一个端口的请求。

先看一个简单的http server中使用cluster的例子:

  1. const cluster = require('cluster');
  2. const http = require('http');
  3. const numCPUs = require('os').cpus().length;
  4. if (cluster.isMaster) {
  5. console.log(`主进程 ${process.pid} 正在运行`);
  6. // 衍生工作进程。
  7. for (let i = 0; i < numCPUs; i++) {
  8. cluster.fork();
  9. }
  10. cluster.on('exit', (worker, code, signal) => {
  11. console.log(`工作进程 ${worker.process.pid} 已退出`);
  12. });
  13. } else {
  14. // 工作进程可以共享任何 TCP 连接。
  15. // 在本例子中,共享的是 HTTP 服务器。
  16. http.createServer((req, res) => {
  17. res.writeHead(200);
  18. res.end('你好世界n');
  19. }).listen(8000);
  20. console.log(`工作进程 ${process.pid} 已启动`);
  21. }

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掉。

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

exit事件会在任何一个工作进程关闭的时候触发。一般用来监测cluster中某一个进程是否异常退出,如果退出的话使用cluster.fork创建新的进程,以保证有足够多的进程来处理请求。

  1. cluster.on('exit', (worker, code, signal) => {
  2. console.log('工作进程 %d 关闭 (%s). 重启中...',
  3. worker.process.pid, signal || code);
  4. cluster.fork();
  5. });

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

  1. const timeouts = [];
  2. function errorMsg() {
  3. console.error('连接出错');
  4. }
  5. cluster.on('fork', (worker) => {
  6. timeouts[worker.id] = setTimeout(errorMsg, 2000);
  7. });

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

    1. cluster.on('listening', (worker, address) => {
    2. console.log(
    3. `工作进程已连接到 ${address.address}:${address.port}`);
    4. });

      其中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。

      1. cluster.disconnect([callback])

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

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

      1. cluster.fork([env])

      fork方法,会从主进程中创建新的子进程。其中env是要添加到进程环境变量的键值对。

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

      最后一个方法是setupMaster:

      1. cluster.setupMaster([settings])

      默认情况下,cluster通过fork方法来创建子进程,但是我们可以通过setupMaster来改变这个行为。通过设置settings变量,我们可以改变后面fork子进程的行为。

      我们看一个setupMaster的例子:

      1. const cluster = require('cluster');
      2. cluster.setupMaster({
      3. exec: 'worker.js',
      4. args: ['--use', 'https'],
      5. silent: true
      6. });
      7. cluster.fork(); // https 工作进程
      8. cluster.setupMaster({
      9. exec: 'worker.js',
      10. args: ['--use', 'http']
      11. });
      12. cluster.fork(); // http 工作进程

      cluster中的属性

      通过cluster对象,我们可以通过isMaster和isWorker来判断进程是否主进程。

      可以通过worker来获取当前工作进程对象的引用:

      1. const cluster = require('cluster');
      2. if (cluster.isMaster) {
      3. console.log('这是主进程');
      4. cluster.fork();
      5. cluster.fork();
      6. } else if (cluster.isWorker) {
      7. console.log(`这是工作进程 #${cluster.worker.id}`);
      8. }

      可以通过workers来遍历活跃的工作进程对象:

      1. // 遍历所有工作进程。
      2. function eachWorker(callback) {
      3. for (const id in cluster.workers) {
      4. callback(cluster.workers[id]);
      5. }
      6. }
      7. eachWorker((worker) => {
      8. worker.send('通知所有工作进程');
      9. });

      每个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 来区分是主动退出还是被动退出,主进程可以根据这个值决定是否重新生成工作进程。

      1. cluster.on('exit', (worker, code, signal) => {
      2. if (worker.exitedAfterDisconnect === true) {
      3. console.log('这是自发退出,无需担心');
      4. }
      5. });
      6. // 杀死工作进程。
      7. worker.kill();

      worker还支持6个方法,分别是:send,kill,destroy,disconnect,isConnected,isDead。

      这里我们主要讲解一下send方法来发送消息:

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

      可以看到send方法和child_process中的send方法参数其实是很类似的。而本质上,worker.send在主进程中,这会发送消息给特定的工作进程。 相当于 ChildProcess.send()。在工作进程中,这会发送消息给主进程。 相当于 process.send()。

      1. if (cluster.isMaster) {
      2. const worker = cluster.fork();
      3. worker.send('你好');
      4. } else if (cluster.isWorker) {
      5. process.on('message', (msg) => {
      6. process.send(msg);
      7. });
      8. }

      在上面的例子中,如果是在主进程中,那么可以使用worker.send来发送消息。而在子进程中,则可以使用worker中的全局变量process来发送消息。

      总结

      使用cluster可以充分使用多核CPU的优势,希望大家在实际的项目中应用起来。

       

      版权说明
      文章采用: 《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权。
      版权声明:未标注转载均为本站原创,转载时请以链接形式注明文章出处。如有侵权、不妥之处,请联系站长删除。敬请谅解!

      -- 展开阅读全文 --
      这篇文章最后更新于2021-2-1,已超过 1 年没有更新,如果文章内容或图片资源失效,请留言反馈,我们会及时处理,谢谢!
      教你分分钟搞定java中的类和object
      « 上一篇
      压缩包密码爆破神器价值1K压缩包密码爆破软件
      下一篇 »

      发表评论

      • 泡泡
      • 阿呆
      • 阿鲁


      扫一扫,支付10
      支付完成后,请等待5秒左右。手机请点此支付!