概述

基本用法

Node.js默认单进程运行,对于32位系统最高可以使用512MB内存,对于64位最高可以使用1GB内存。对于多核CPU的计算机来说,这样做效率很低,因为只有一个核在运行,其他核都在闲置。cluster模块就是为了解决这个问题而提出的。

cluster模块允许设立一个主进程和若干个worker进程,由主进程监控和协调worker进程的运行。worker之间采用进程间通信交换消息,cluster模块内置一个负载均衡器,采用Round-robin算法协调各个worker进程之间的负载。运行时,所有新建立的链接都由主进程完成,然后主进程再把TCP连接分配给指定的worker进程。

var cluster = require('cluster');
var os = require('os');if (cluster.isMaster){for (var i = 0, n = os.cpus().length; i < n; i += 1){cluster.fork();}
} else {http.createServer(function(req, res) {res.writeHead(200);res.end("hello world\n");}).listen(8000);
}复制代码

上面代码先判断当前进程是否为主进程(cluster.isMaster),如果是的,就按照CPU的核数,新建若干个worker进程;如果不是,说明当前进程是worker进程,则在该进程启动一个服务器程序。

上面这段代码有一个缺点,就是一旦work进程挂了,主进程无法知道。为了解决这个问题,可以在主进程部署online事件和exit事件的监听函数。

var cluster = require('cluster');if(cluster.isMaster) {var numWorkers = require('os').cpus().length;console.log('Master cluster setting up ' + numWorkers + ' workers...');for(var i = 0; i < numWorkers; i++) {cluster.fork();}cluster.on('online', function(worker) {console.log('Worker ' + worker.process.pid + ' is online');});cluster.on('exit', function(worker, code, signal) {console.log('Worker ' + worker.process.pid + ' died with code: ' + code + ', and signal: ' + signal);console.log('Starting a new worker');cluster.fork();});
}复制代码

上面代码中,主进程一旦监听到worker进程的exit事件,就会重启一个worker进程。worker进程一旦启动成功,可以正常运行了,就会发出online事件。

worker对象

worker对象是cluster.fork()的返回值,代表一个worker进程。

它的属性和方法如下。

(1)worker.id

worker.id返回当前worker的独一无二的进程编号。这个编号也是cluster.workers中指向当前进程的索引值。

(2)worker.process

所有的worker进程都是用child_process.fork()生成的。child_process.fork()返回的对象,就被保存在worker.process之中。通过这个属性,可以获取worker所在的进程对象。

(3)worker.send()

该方法用于在主进程中,向子进程发送信息。

if (cluster.isMaster) {var worker = cluster.fork();worker.send('hi there');
} else if (cluster.isWorker) {process.on('message', function(msg) {process.send(msg);});
}复制代码

上面代码的作用是,worker进程对主进程发出的每个消息,都做回声。

在worker进程中,要向主进程发送消息,使用process.send(message);要监听主进程发出的消息,使用下面的代码。

process.on('message', function(message) {console.log(message);
});复制代码

发出的消息可以字符串,也可以是JSON对象。下面是一个发送JSON对象的例子。

worker.send({type: 'task 1',from: 'master',data: {// the data that you want to transfer}
});复制代码

cluster.workers对象

该对象只有主进程才有,包含了所有worker进程。每个成员的键值就是一个worker进程对象,键名就是该worker进程的worker.id属性。

function eachWorker(callback) {for (var id in cluster.workers) {callback(cluster.workers[id]);}
}
eachWorker(function(worker) {worker.send('big announcement to all workers');
});复制代码

上面代码用来遍历所有worker进程。

当前socket的data事件,也可以用id属性识别worker进程。

socket.on('data', function(id) {var worker = cluster.workers[id];
});复制代码

cluster模块的属性与方法

isMaster,isWorker

isMaster属性返回一个布尔值,表示当前进程是否为主进程。这个属性由process.env.NODE_UNIQUE_ID决定,如果process.env.NODE_UNIQUE_ID为未定义,就表示该进程是主进程。

isWorker属性返回一个布尔值,表示当前进程是否为work进程。它与isMaster属性的值正好相反。

fork()

fork方法用于新建一个worker进程,上下文都复制主进程。只有主进程才能调用这个方法。

该方法返回一个worker对象。

kill()

kill方法用于终止worker进程。它可以接受一个参数,表示系统信号。

如果当前是主进程,就会终止与worker.process的联络,然后将系统信号法发向worker进程。如果当前是worker进程,就会终止与主进程的通信,然后退出,返回0。

在以前的版本中,该方法也叫做 worker.destroy() 。

listening事件

worker进程调用listening方法以后,“listening”事件就传向该进程的服务器,然后传向主进程。

该事件的回调函数接受两个参数,一个是当前worker对象,另一个是地址对象,包含网址、端口、地址类型(IPv4、IPv6、Unix socket、UDP)等信息。这对于那些服务多个网址的Node应用程序非常有用。

cluster.on('listening', function (worker, address) {console.log("A worker is now connected to " + address.address + ":" + address.port);
});复制代码

不中断地重启Node服务

思路

重启服务需要关闭后再启动,利用cluster模块,可以做到先启动一个worker进程,再把原有的所有work进程关闭。这样就能实现不中断地重启Node服务。

首先,主进程向worker进程发出重启信号。

workers[wid].send({type: 'shutdown', from: 'master'});复制代码

worker进程监听message事件,一旦发现内容是shutdown,就退出。

process.on('message', function(message) {if(message.type === 'shutdown') {process.exit(0);}
});复制代码

下面是一个关闭所有worker进程的函数。

function restartWorkers() {var wid, workerIds = [];for(wid in cluster.workers) {workerIds.push(wid);}workerIds.forEach(function(wid) {cluster.workers[wid].send({text: 'shutdown',from: 'master'});setTimeout(function() {if(cluster.workers[wid]) {cluster.workers[wid].kill('SIGKILL');}}, 5000);});
};复制代码

实例

下面是一个完整的实例,先是主进程的代码master.js。

var cluster = require('cluster');console.log('started master with ' + process.pid);// 新建一个worker进程
cluster.fork();process.on('SIGHUP', function () {console.log('Reloading...');var new_worker = cluster.fork();new_worker.once('listening', function () {// 关闭所有其他worker进程for(var id in cluster.workers) {if (id === new_worker.id.toString()) continue;cluster.workers[id].kill('SIGTERM');}});
});复制代码

上面代码中,主进程监听SIGHUP事件,如果发生该事件就关闭其他所有worker进程。之所以是SIGHUP事件,是因为nginx服务器监听到这个信号,会创造一个新的worker进程,重新加载配置文件。另外,关闭worker进程时,主进程发送SIGTERM信号,这是因为Node允许多个worker进程监听同一个端口。

下面是worker进程的代码server.js。

var cluster = require('cluster');if (cluster.isMaster) {require('./master');return;
}var express = require('express');
var http = require('http');
var app = express();app.get('/', function (req, res) {res.send('ha fsdgfds gfds gfd!');
});http.createServer(app).listen(8080, function () {console.log('http://localhost:8080');
});复制代码

使用时代码如下。

$ node server.js
started master with 10538
http://localhost:8080复制代码

然后,向主进程连续发出两次SIGHUP信号。

$ kill -SIGHUP 10538
$ kill -SIGHUP 10538复制代码

主进程会连续两次新建一个worker进程,然后关闭所有其他worker进程,显示如下。

Reloading...
http://localhost:8080
Reloading...
http://localhost:8080复制代码

最后,向主进程发出SIGTERM信号,关闭主进程。

$ kill 10538复制代码

PM2模块

PM2模块是cluster模块的一个包装层。它的作用是尽量将cluster模块抽象掉,让用户像使用单进程一样,部署多进程Node应用。

// app.js
var http = require('http');http.createServer(function(req, res) {res.writeHead(200);res.end("hello world");
}).listen(8080);复制代码

上面代码是标准的Node架设Web服务器的方式,然后用PM2从命令行启动这段代码。

$ pm2 start app.js -i 4复制代码

上面代码的i参数告诉PM2,这段代码应该在cluster_mode启动,且新建worker进程的数量是4个。如果i参数的值是0,那么当前机器有几个CPU内核,PM2就会启动几个worker进程。

如果一个worker进程由于某种原因挂掉了,会立刻重启该worker进程。

# 重启所有worker进程
$ pm2 reload all复制代码

每个worker进程都有一个id,可以用下面的命令查看单个worker进程的详情。

$ pm2 show <worker id>复制代码

正确情况下,PM2采用fork模式新建worker进程,即主进程fork自身,产生一个worker进程。pm2 reload命令则会用spawn方式启动,即一个接一个启动worker进程,一个新的worker启动成功,再杀死一个旧的worker进程。采用这种方式,重新部署新版本时,服务器就不会中断服务。

$ pm2 reload <脚本文件名>复制代码

关闭worker进程的时候,可以部署下面的代码,让worker进程监听shutdown消息。一旦收到这个消息,进行完毕收尾清理工作再关闭。

process.on('message', function(msg) {if (msg === 'shutdown') {close_all_connections();delete_logs();server.close();process.exit(0);}
});
复制代码

转载于:https://juejin.im/post/5ce53c856fb9a07ee27aecb8

Node js 集群(cluster)相关推荐

  1. node php mysql 性能_使用mysql池对node.js(集群)的性能进行基准测试:Lighttpd PHP?

    编辑(2):现在使用db- mysql和generic-pool模块.错误率显着下降,徘徊在13%,但吞吐量仍然在100 req / sec左右. 编辑(1):在有人建议ORDER BY RAND() ...

  2. node redis集群_如何使用集群扩展Node.js服务器

    node redis集群 by Michele Riva 由Michele Riva 如何使用集群扩展Node.js服务器 (How to scale your Node.js server usin ...

  3. 什么是集群(cluster)

    1.集群 1.1 什么是集群 简单的说,集群(cluster)就是一组计算机,它们作为一个总体向用户提供一组网络资源.这些单个的计算机系统就是集群的节点(node).一个理想的集群是,用户从来不会意识 ...

  4. MySQL数据库(九) 集群 Cluster 和性能优化

    文章目录 6 MySQL 集群 Cluster 6.1 MySQL主从复制 6.1.1 主从复制架构和原理 6.1.2 实现主从复制配置 6.1.3 主从复制相关 6.1.4 实现级联复制 6.1.5 ...

  5. 6 MySQL 集群Cluster

    文章目录 6 MySQL 集群Cluster 6.1 MySQL 主从复制 6.1.1 主从复制架构和原理 6.1.1.1 服务性能扩展方式 6.1.1.2 MySQL的扩展 6.1.1.3 复制的功 ...

  6. Elasticsearch - Elasticsearch集群Cluster(三)

    阅读本文前可先参考 https://blog.csdn.net/MinggeQingchun/article/details/126618387 一.单机 & 集群 1.单机 单台 Elast ...

  7. Redis集群Cluster部署

    这篇Redis 集群部署笔记参考的书籍资料是: <Redis入门指南>第二版,作者:李子骅 第8章 <Redis开发与运维> 作者:付磊 第10章 以下是学习笔记,记录了Red ...

  8. 【Redis】集群(cluster)

    文章目录 Redis集群(cluster) 定义 官网 作用 集群算法(分片->槽位) 分片(每台Redis实例) 槽位 slot槽位映射 哈希取余分区 一致性哈希算法分区 哈希槽分区 搭建三主 ...

  9. redis集群扩容和缩容_redis集群cluster搭建,扩容缩容

    1.    概要说明 1.1.    概述 本文旨在说明平台部署环境搭建的过程和详细步骤说明,为后面系统部署提供清晰的参考依照. 以下搭建部署的环境,为一个小型的分布式集群环境.满足负载均衡和单点故障 ...

最新文章

  1. 《数据科学家养成手册》第十一章----算法学(非线性回归)
  2. IOS学习笔记(四)之UITextField和UITextView控件学习
  3. 点击php文件显示下载文件,求助 为什么编的下载文件代码,打开后下的全是php文件...
  4. ubuntu kvm 部署安装   快照
  5. [译]RabbitMQ教程C#版 - 发布订阅
  6. input禁止鼠标滚轮改变数值
  7. (229)DPU加速数据处理
  8. 计算机视觉硕士课程南京大学,南京大学计算机系研究生课程文库
  9. 简便word批量打印 - Office批量打印精灵(支持正版)
  10. 22-5 论如何将标准中国地图矢量化并且导入arcgis中
  11. 3西格玛计算公式_六西格玛相关参数及计算公式
  12. [JS]Tue Feb 01 2022 00:00:00 GMT+0800 (中国标准时间) 日期格式化
  13. 白盒测试和黑盒测试(感谢东子哥作答)
  14. win10计算机管理不可用,win10管理员被禁用怎么办,win10怎么管理员运行
  15. 大数据毕设选题 - 机器学习餐厅销量预测(大数据 python)
  16. 在python里是什么意思_在Python中$是什么意思?
  17. orcad capture 快速制作原理图库
  18. python语言创意绘画是什么-Python街机模块的draw系列绘画例子集合
  19. 第一章计算机系统基础知识,计算机系统基础知识 第一章(未完待续)
  20. VC-VQA: Visual Calibration Mechanism for Visual Question Answering (VQA的视觉校准机制)

热门文章

  1. C#.NET Winform 通用开发框架
  2. java 抽奖算法_Java抽奖算法第二例
  3. 易语言程序加密的原则
  4. Docker安装ElasticSerach、ElasticSerachhead、IK分词器及Kibanna步骤(亲测能用)
  5. 【翻译】.NET 5中的性能改进
  6. 如何查看快门使用次数v2
  7. 分享116个JS图片代码,总有一款适合您
  8. RocksDB数据库简介及使用分享
  9. HY-SRF05超声波测距模块的使用
  10. 怎么把java安装进老年机_环境搭建 - 老人与JAVA - 博客园