nodejs cluster模块初探
大家都知道nodejs是一个单进程单线程的服务器引擎,不管有多么的强大硬件,只能利用到单个CPU进行计算。所以,为了使用多核cpu来提高性能 就有了cluster,让node可以利用多核CPU实现并行。
随着nodejs的发展,让nodejs上生产环境,就必须是支持多进程多核处理!在V0.6.0版本,Nodejs内置了cluster的特性。自此,Nodejs终于可以作为一个独立的应用开发解决方案,映入大家眼帘了。
这个包不用安装,node默认自带。
引入cluster
const cluster = require('cluster');
如果你有多进程使用的经验,就一定知道一般来说很多多进程的模型都是 一个主进程,然后fork一堆工作进程。我们这里也不例外。 下面看一个简单的例子。
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
}
这个demo首先引入了 cluster 然后判断是否是主进程,如果是的话 进入主进程 创建 cpu个数 个子进程。然后监听子进程exit
子进程if判断未通过,所以进入else 执行创建http代码
这样就可以简单的实现了一个单机多进程的httpd服务器。
#### 初窥
nodejs把进程封装的很全面,所以以前有c基础的同学再看node就会感觉非常简单。大家都知道在c里面 我们是通过fork的返回值判断到底是主进程还是子进程 那么在node里面 isMaster 到底是何方神圣呢。
其实node会在子进程里面添加一个环境变量
module.exports = ('NODE_UNIQUE_ID' in process.env) ?
require('internal/cluster/child') :
require('internal/cluster/master');
只需要判断当前进程有没有环境变量“NODE_UNIQUE_ID”就可知道当前进程是否是主进程;而变量“NODE_UNIQUE_ID”则是在主进程fork子进程时传递进去的参数,因此采用cluster.fork创建的子进程是一定包含“NODE_UNIQUE_ID”的。
消息通信
nodejs为我们屏蔽了底层的多进程信息通信,提供了一个 `send` 和 `on` 事件来传递消息,不过只能子进程和父进程传递,如果我们要2个子进程传递的话必须通过主进程做中转。
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();
(function (i) {
workers[i].on('message', function(message) {
if (message.cmd == 'start') {
console.log(JSON.stringify(message));
}
});
workers[i].send({cmd:'callback',msg:"收到收到"});
})(i);
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world\n');
process.send({cmd: 'start',msg:"我已准备好!"});
}).listen(8000);
}
上面代码我们通过 子进程 `process.send` 发送给父进程,然后父进程接收到消息后 通过`send`回传的一个简单demo。
疑惑,多个子进程可以监听一个端口吗?
大家看到上面的几个例子,都是多个子进程监听同一个8000端口,那么有同学会有疑问,多个socket是否可以同时监听一个端口呢.
其实 cluster 实现同时多个进程监听一个端口,有两种方法。
1、 RoundRobin 默认*
主进程每fork一个子进程,都会调用handoff函数,进入该子进程的处理循环中。一旦主进程没有缓存的客户端请求时(this.handles为空),便会将当前子进程加入free空闲队列,等待主进程的下一步调度。这就是cluster模式的RoundRobin调度策略,每个子进程的处理逻辑都是一个闭环,直到主进程缓存的客户端请求处理完毕时,该子进程的处理闭环才被打开。在这个模式中,其实子进程 listen是个假的,并不会真正的监听端口,而是主进程来监听端口,如果有请求过来,就会调用其中的一个子进程来处理。

2、shared socket (windows默认)
shared socket策略(后文简称SS策略)采用SS策略调度算法,子进程的服务器工作逻辑完全不同于上文中所讲的那样,子进程创建的TCP服务器会在底层侦听端口并处理响应,这是如何实现的呢?SS策略的核心在于IPC传输句柄的文件描述符,并且在C++层设置端口的**SO_REUSEADDR(端口复用)** 选项,最后根据传输的文件描述符还原出handle(net.TCP),处理请求。这正是shared socket名称由来,共享文件描述符。
既然SS策略传递的是master进程的服务端socket的文件描述符,子进程侦听该描述符,那么由谁来调度哪个子进程处理请求呢?这就是由操作系统内核来进行调度。可是内核调度往往出现意想不到的效果,在linux下导致请求往往集中在某几个子进程中处理。这从内核的调度策略也可以推算一二,内核的进程调度离不开上下文切换,上下文切换的代价很高,不仅需要保存当前进程的代码、数据和堆栈等用户空间数据,还需要保存各种寄存器,如PC,ESP,最后还需要恢复被调度进程的上下文状态,仍然包括代码、数据和各种寄存器,因此代价非常大。而linux内核在调度这些子进程时往往倾向于唤醒最近被阻塞的子进程,上下文切换的代价相对较小。而且内核的调度策略往往受到当前系统的运行任务数量和资源使用情况,对专注于业务开发的http服务器影响较大,因此会造成某些子进程的负载严重不均衡的状况。那么为什么cluster模块默认会在windows机器中采用SS策略调度子进程呢?原因是node在windows平台采用的IOCP来最大化性能,它使得传递连接的句柄到其他进程的成本很高,因此采用默认的依靠操作系统调度的SS策略。
今天就大概写到这里,其实我对cluster了解的也不算很多,这篇文章就当是个抛砖引玉吧。欢迎大家一起来共同学习。
cluster 常用对象
cluster的各种属性和函数
cluster.setttings:配置集群参数对象
cluster.isMaster:判断是不是master节点
cluster.isWorker:判断是不是worker节点
Event: 'fork': 监听创建worker进程事件
Event: 'online': 监听worker创建成功事件
Event: 'listening': 监听worker向master状态事件
Event: 'disconnect': 监听worker断线事件
Event: 'exit': 监听worker退出事件
Event: 'setup': 监听setupMaster事件
cluster.setupMaster([settings]) : 设置集群参数
cluster.fork([env]): 创建worker进程
cluster.disconnect([callback]): 关闭worket进程
cluster.worker: 获得当前的worker对象
cluster.workers: 获得集群中所有存活的worker对象 worker对象
worker的各种属性和函数:可以通过cluster.workers, cluster.worket获得。
worker.id: 进程ID号
worker.process: ChildProcess对象
worker.suicide: 在disconnect()后,判断worker是否自杀
worker.send(message, [sendHandle]): master给worker发送消息。注:worker给发master发送消息要用process.send(message)
worker.kill([signal='SIGTERM']): 杀死指定的worker,别名destory()
worker.disconnect(): 断开worker连接,让worker自杀
Event: 'message': 监听master和worker的message事件
Event: 'online': 监听指定的worker创建成功事件
Event: 'listening': 监听master向worker状态事件
Event: 'disconnect': 监听worker断线事件
Event: 'exit': 监听worker退出事件
参考文档
Nodejs cluster模块深入探究
https://cnodejs.org/topic/596ffb9b3f0ab31540ed4b91
官方文档
https://nodejs.org/api/cluster.html
nodejs cluster模块初探的更多相关文章
- Nodejs cluster模块深入探究
由表及里 HTTP服务器用于响应来自客户端的请求,当客户端请求数逐渐增大时服务端的处理机制有多种,如tomcat的多线程.nginx的事件循环等.而对于node而言,由于其也采用事件循环和异步I/O机 ...
- Nodejs中cluster模块的多进程共享数据问题
Nodejs中cluster模块的多进程共享数据问题 前述 nodejs在v0.6.x之后增加了一个模块cluster用于实现多进程,利用child_process模块来创建和管理进程,增加程序在多核 ...
- nodejs(四) --- cluster模块详解
什么是cluster模块,为什么需要cluster模块? cluster在英文中有集.群的意思. nodejs默认是单进程的,但是对于多核的cpu来说, 单进程显然没有充分利用cpu,所以,node ...
- nodejs中的子进程,深入解析child_process模块和cluster模块
Node.js的进程管理 node遵循的是单线程单进程的模式,node的单线程是指js的引擎只有一个实例,且在nodejs的主线程中执行,同时node以事件驱动的方式处理IO等异步操作.node的 ...
- 【nodejs原理&源码赏析(4)】深度剖析cluster模块源码与node.js多进程(上)
[摘要] 集群管理模块cluster浅析 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 概述 cluster模块是node.js中用于实现和管理 ...
- 【nodejs原理&源码赏析(4)】深度剖析cluster模块源码与node.js多进程(上)
目录 一. 概述 二. 线程与进程 三. cluster模块源码解析 3.1 起步 3.2 入口 3.3 主进程模块master.js 3.4 子进程模块child.js 四. 小结 示例代码托管在: ...
- 【nodejs原理&源码赏析(6)】深度剖析cluster模块源码与node.js多进程(下)
[摘要] cluster模块详解 示例代码托管在:http://www.github.com/dashnowords/blogs 阅读本章需要先阅读本系列前两章内容预热一下. 一. 引言 前两篇博文中 ...
- 【nodejs原理&源码赏析(6)】深度剖析cluster模块源码与node.js多进程(下)
目录 一. 引言 二.server.listen方法 三.cluster._getServer( )方法 四.跨进程通讯工具方法Utils 五.act:queryServer消息 六.轮询调度Roun ...
- Node.js的cluster模块——Web后端多进程服务
众所周知,Node.js是单线程的,一个单独的Node.js进程无法充分利用多核.Node.js从v0.6.0开始,新增cluster模块,让Node.js开发Web服务时,很方便的做到充分利用多核机 ...
随机推荐
- c#中常用集合类和集合接口之接口系列【转】
常用集合接口系列:http://www.cnblogs.com/fengxiaojiu/p/7997704.html 常用集合类系列:http://www.cnblogs.com/fengxiaoji ...
- ubuntu下查看-卸载软件(卸载.net core sdk的方法)
查看已安装的包:dpkg --list 查看正则匹配的包:dpkg --list 'dotnet-*' //查看以dotnet-开头的包 卸载匹配的包:sudo apt-get --purge rem ...
- 2017ecjtu-summer training #5 UVA10382
题意 问最少可用几个圆覆盖矩形区域. 解析 将圆形转换成矩形有效区域,直径小于等于宽度的圆不考虑,从而转化成区间覆盖问题,然后贪心出最少圆. 贪心思想 每次选择出区域左界比上次选出的区域右界小的且区域 ...
- NYoj_171聪明的kk
聪明的kk 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 聪明的"KK" 非洲某国展馆的设计灵感源于富有传奇色彩的沙漠中陡然起伏的沙丘,体现出本国不 ...
- 电脑打不开网页,使用dns优化下就可以了。
通过电脑管家dns优化下就可以了.启用114DNS
- iframe及与页面之间的通信
获取iframe对象 iframe元素本身是位于父级页面中的,所以你可以像一个普通元素一样的使用和操作它 代表了iframe内容window对象是作为一个页面的属性加入到iframe中的, 为了让父级 ...
- Hive_UDF函数中集合对象初始化的注意事项
UDF函数中定义的集合对象何时初始化 udf函数放在sql中对某个字段进行处理,那么在底层会创建一个该类的对象,这个对象不断的去调用这个evaluate(...)方法,截图如下: 1.1 如果说对 ...
- Sublime Text 使用介绍、全套快捷键及插件推荐
开篇:如果说Notepad++是一款不错Code神器,那么Sublime Text应当称得上是神器滴哥.Sublime Text最大的优点就是跨平台,Mac和Windows均可完美使用:其次是强大的插 ...
- P2P结构与Quorum机制------《Designing Data-Intensive Applications》读书笔记8
前文涉及到了很多与Leader相关的算法,大家有木有想过,王侯将相,宁有种乎,既然Leader这么麻烦,干脆还是采用P2P模型吧,来个大家平等的架构.本篇需要和大家探讨的就是多副本下实现民主政治的Qu ...
- Java中 equals() 和 == 的区别
1)对于==,如果作用于基本数据类型的变量,则直接比较其存储的 "值"是否相等: 如果作用于引用类型的变量,则比较的是所指向的对象的地址 2)对于equals方法,注意:equal ...