在nodejs中建立cluster

在nodejs中建立clusternode

简介

在前面的文章中,咱们讲到了能够经过worker_threads来建立新的线程,可使用child_process来建立新的子进程。本文将会介绍如何建立nodejs的集群cluster。服务器

cluster集群

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

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

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

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

cluster支持7中event,分别是disconnect,exit,fork,listening,message,online和setup。server

在讲解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的博客

欢迎关注个人公众号:「程序那些事」最通俗的解读,最深入的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

相关文章
相关标签/搜索