在nodejs中创建cluster

简介

在前面的文章中,我们讲到了可以通过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的博客

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

在nodejs中创建cluster的更多相关文章

  1. 在nodejs中创建child process

    目录 简介 child process 异步创建进程 同步创建进程 在nodejs中创建child process 简介 nodejs的main event loop是单线程的,nodejs本身也维护 ...

  2. nodejs中创建web服务,监听本地IP

    nodejs官网例子 var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {' ...

  3. nodeJs中创建服务器

    var http=require('http'); var httpObj=http.createServer(function(req,res) { console.log('someBody:' ...

  4. Nodejs中cluster模块的多进程共享数据问题

    Nodejs中cluster模块的多进程共享数据问题 前述 nodejs在v0.6.x之后增加了一个模块cluster用于实现多进程,利用child_process模块来创建和管理进程,增加程序在多核 ...

  5. nodejs中的子进程,深入解析child_process模块和cluster模块

    Node.js的进程管理   node遵循的是单线程单进程的模式,node的单线程是指js的引擎只有一个实例,且在nodejs的主线程中执行,同时node以事件驱动的方式处理IO等异步操作.node的 ...

  6. Nodejs express中创建ejs项目,解决express下默认创建jade,无法创建ejs问题

    最近在看<Node.js开发指南>,看到使用nodejs进行web开发的时候,准备创建ejs项目遇到问题了, 书上命令为: express -t ejs microblog 可是执行后,仍 ...

  7. Nodejs express中创建ejs项目 error install Couldn't read dependencies

    最近在看<Node.js开发指南>,看到使用nodejs进行web开发的时候,准备创建ejs项目遇到问题了   书上命令为:   express -t ejs microblog 可是执行 ...

  8. nodejs中使用worker_threads来创建新的线程

    目录 简介 worker_threads isMainThread MessageChannel parentPort和MessagePort markAsUntransferable SHARE_E ...

  9. 在NodeJS中使用Redis缓存数据

    Redis数据库采用极简的设计思想,最新版的源码包还不到2Mb.其在使用上也有别于一般的数据库. node_redis redis驱动程序多使用 node_redis 此模块可搭载官方的 hiredi ...

随机推荐

  1. 5. Longest Palindromic Substring最大回文子串

    int sta = 0; int max = 1; public String longestPalindrome(String s) { /* 判断回文有两种: 1.最大回文子序列求长度: 用动态规 ...

  2. C语言实现汉诺塔

    汉诺塔 要把A柱子上的盘子移动到C柱子上,在移动过程中可以借助B柱子,但是要求小的盘子在上大的盘子在下. 解题思路: 1.把A柱子上的前N-1个盘子借助C柱子,全部移动到B柱子上(过程暂不考虑),再把 ...

  3. 详解Js加法运算符

    简介 JavaScript是一门了不起的语言.我喜欢它的灵活性:只需以你喜欢的方式做事:更改变量类型,动态的向对象添加方法或属性,对不同的变量类型使用运算符等等. 然而动态是要付出代价的,开发人员需要 ...

  4. 【Termux】使用指南

    直入主题: 1.1 下载安装 Google下载(有条件的用!) F-droid下载(建议使用!) ps:不建议去酷安下载! 1.2 配置 apt update && apt upgra ...

  5. esx.problem.hyperthreading.unmitigated

    是因为VMware新发布的一个漏洞补丁导致的具体解释可参阅VMware官方kb,有详细解释和版本说明. 可选择屏蔽该问题告警 选中主机>配置>高级系统设置>编辑>修改" ...

  6. Hive日期函数总结(转学习使用)

    一.时间戳函数 1.获取当前时区的UNIX时间戳:select unix_timestamp(); 2.将指定时间转为UNIX时间戳: select unix_timestamp('2012-03-0 ...

  7. Label_strange_labels

    空格符号  特殊符号名 注释语句 , align = "left right center" 粗体 斜体 也可能是控制标签 上标 下标 大字号 小字号 下划线 删除线 等宽 键盘输 ...

  8. 查找linux系统下的端口被占用进程的两种方法 【转】

    在linux下开发时,你的软件可能要使用某一个端口,或者想查找某一个端口是否被占用.需要怎么做呢??这的确是一个比较烦恼的问题,我也此为这个苦恼过.但是通过查找man手册,还是同事的交流.总结出来两种 ...

  9. 用js实现打印九九乘法表

    用js在打印九九乘法表 思考 在学习了流程控制和条件判断后,我们可以利用js打印各式各样的九九乘法表 不管是打印什么样三角形九九乘法表,我们都应该找到有规律的地方,比如第一列的数字是什么规律,第一行的 ...

  10. Mac pycharm更换版本后打不开

    1.第一步:先输入:   cd /Applications/PyCharm.app/Contents/MacOS 2.第二步:查看无法打开pycharm的原因,需要输入:c./pycharm 3.第三 ...