大家都知道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模块初探的更多相关文章

  1. Nodejs cluster模块深入探究

    由表及里 HTTP服务器用于响应来自客户端的请求,当客户端请求数逐渐增大时服务端的处理机制有多种,如tomcat的多线程.nginx的事件循环等.而对于node而言,由于其也采用事件循环和异步I/O机 ...

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

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

  3. nodejs(四) --- cluster模块详解

    什么是cluster模块,为什么需要cluster模块?  cluster在英文中有集.群的意思. nodejs默认是单进程的,但是对于多核的cpu来说, 单进程显然没有充分利用cpu,所以,node ...

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

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

  5. 【nodejs原理&源码赏析(4)】深度剖析cluster模块源码与node.js多进程(上)

    [摘要] 集群管理模块cluster浅析 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 概述 cluster模块是node.js中用于实现和管理 ...

  6. 【nodejs原理&源码赏析(4)】深度剖析cluster模块源码与node.js多进程(上)

    目录 一. 概述 二. 线程与进程 三. cluster模块源码解析 3.1 起步 3.2 入口 3.3 主进程模块master.js 3.4 子进程模块child.js 四. 小结 示例代码托管在: ...

  7. 【nodejs原理&源码赏析(6)】深度剖析cluster模块源码与node.js多进程(下)

    [摘要] cluster模块详解 示例代码托管在:http://www.github.com/dashnowords/blogs 阅读本章需要先阅读本系列前两章内容预热一下. 一. 引言 前两篇博文中 ...

  8. 【nodejs原理&源码赏析(6)】深度剖析cluster模块源码与node.js多进程(下)

    目录 一. 引言 二.server.listen方法 三.cluster._getServer( )方法 四.跨进程通讯工具方法Utils 五.act:queryServer消息 六.轮询调度Roun ...

  9. Node.js的cluster模块——Web后端多进程服务

    众所周知,Node.js是单线程的,一个单独的Node.js进程无法充分利用多核.Node.js从v0.6.0开始,新增cluster模块,让Node.js开发Web服务时,很方便的做到充分利用多核机 ...

随机推荐

  1. python写zip破解器

    浏览桌面依然平静,!!!!等等..怎么有个压缩包 打开一看!!!156.txt???waht the fuck? 卧槽还有密码!!!!!! 但是我不知道╮(╯▽╰)╭该怎么办呢! 很简单,python ...

  2. THUSC2016 游记

    浑浑噩噩地就出发了,只记得可以翘课,不知道自己要干什么去. Day 0    5点起床,到潮汕机场坐飞机.第一次坐飞机非常不爽起飞和降落时的加速度……终于还是转转地铁.动车在下午4点左右抵达目的地,西 ...

  3. POJ 1163 The Triangle【dp+杨辉三角加强版(递归)】

    The Triangle Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 49955   Accepted: 30177 De ...

  4. [51nod1597]有限背包计数问题

    你有一个大小为n的背包,你有n种物品,第i种物品的大小为i,且有i个,求装满这个背包的方案数有多少 两种方案不同当且仅当存在至少一个数i满足第i种物品使用的数量不同 Input 第一行一个正整数n 1 ...

  5. Codeforces780C

    题解:n个气球 从1到n染色,如果a.b和c是不同的正方形,a和b在它们之间有一条直接的路径,b和c之间有一条直接的路径,然后在这三个方块上的气球颜色是不同的. AC代码 #include <s ...

  6. 克鲁斯卡尔(Kruskal)算法求最小生成树

    /* *Kruskal算法求MST */ #include <iostream> #include <cstdio> #include <cstring> #inc ...

  7. [国嵌攻略][107][Linux进程管理子系统]

    进程与程序 1.程序:存放在磁盘上的一系列代码和数据的可执行映像,是一个静止的实体. 2.进程:是一个执行中的程序,它是一个动态的实体. 进程四要素 1.有一段程序供其执行.这段程序不一定是某个进程所 ...

  8. [国嵌笔记][026][ARM伪指令]

    ARM机器码 1.汇编程序通过汇编器变成机器码,然后才能在ARM处理器上运行 2.ARM机器码是一个32位的数,被分成了多个段,每个段都有各自的含义 3.格式: cond:表示条件(4位) I:表示源 ...

  9. JQeury添加和删除class内部实现代码(简化版)

    下面是JQuery对元素class操作的简单实现,请看代码: 添加class: //增加class function addClass(elem,value) { var classes, cur, ...

  10. NYOJ 1249 物资调度(DFS+剪枝)

    题目链接: http://acm.nyist.net/JudgeOnline/problem.php?pid=1249 描述 某地区发生了地震,灾区已经非常困难,灾民急需一些帐篷.衣物.食品和血浆等物 ...