深入剖析Node.js cluster模块


Posted in Javascript onMay 23, 2018

cluster模块概览

node实例是单线程作业的。在服务端编程中,通常会创建多个node实例来处理客户端的请求,以此提升系统的吞吐率。对这样多个node实例,我们称之为cluster(集群)。

借助node的cluster模块,开发者可以在几乎不修改原有项目代码的前提下,获得集群服务带来的好处。

集群有以下两种常见的实现方案,而node自带的cluster模块,采用了方案二。

方案一:多个node实例+多个端口

集群内的node实例,各自监听不同的端口,再由反向代理实现请求到多个端口的分发。

  1. 优点:实现简单,各实例相对独立,这对服务稳定性有好处。
  2. 缺点:增加端口占用,进程之间通信比较麻烦。

方案二:主进程向子进程转发请求

集群内,创建一个主进程(master),以及若干个子进程(worker)。由master监听客户端连接请求,并根据特定的策略,转发给worker。

  1. 优点:通常只占用一个端口,通信相对简单,转发策略更灵活。
  2. 缺点:实现相对复杂,对主进程的稳定性要求较高。

入门实例

在cluster模块中,主进程称为master,子进程称为worker。

例子如下,创建与CPU数目相同的服务端实例,来处理客户端请求。注意,它们监听的都是同样的端口。

// server.js
var cluster = require('cluster');
var cpuNums = require('os').cpus().length;
var http = require('http');

if(cluster.isMaster){
 for(var i = 0; i < cpuNums; i++){
  cluster.fork();
 }
}else{
 http.createServer(function(req, res){
  res.end(`response from worker ${process.pid}`);
 }).listen(3000);

 console.log(`Worker ${process.pid} started`);
}

创建批处理脚本:./req.sh。

#!/bin/bash

# req.sh
for((i=1;i<=4;i++)); do  
 curl http://127.0.0.1:3000
 echo ""
done

输出如下。可以看到,响应来自不同的进程。

response from worker 23735
response from worker 23731
response from worker 23729
response from worker 23730

cluster模块实现原理

了解cluster模块,主要搞清楚3个问题:

  1. master、worker如何通信?
  2. 多个server实例,如何实现端口共享?
  3. 多个server实例,来自客户端的请求如何分发到多个worker?

下面会结合示意图进行介绍,源码级别的介绍,可以参考 笔者的github。

问题1:master、worker如何通信

这个问题比较简单。master进程通过 cluster.fork() 来创建 worker进程。cluster.fork() 内部 是通过 child_process.fork() 来创建子进程。

也就是说:

  1. master进程、worker进程是父、子进程的关系。
  2. master进程、woker进程可以通过IPC通道进行通信。(重要)

问题2:如何实现端口共享

在前面的例子中,多个woker中创建的server监听了同个端口3000。通常来说,多个进程监听同个端口,系统会报错。

为什么我们的例子没问题呢?

秘密在于,net模块中,对 listen() 方法进行了特殊处理。根据当前进程是master进程,还是worker进程:

  1. master进程:在该端口上正常监听请求。(没做特殊处理)
  2. worker进程:创建server实例。然后通过IPC通道,向master进程发送消息,让master进程也创建 server 实例,并在该端口上监听请求。当请求进来时,master进程将请求转发给worker进程的server实例。

归纳起来,就是:master进程监听特定端口,并将客户请求转发给worker进程。

如下图所示:

深入剖析Node.js cluster模块

问题3:如何将请求分发到多个worker

每当worker进程创建server实例来监听请求,都会通过IPC通道,在master上进行注册。当客户端请求到达,master会负责将请求转发给对应的worker。

具体转发给哪个worker?这是由转发策略决定的。可以通过环境变量NODE_CLUSTER_SCHED_POLICY设置,也可以在cluster.setupMaster(options)时传入。

默认的转发策略是轮询(SCHED_RR)。

当有客户请求到达,master会轮询一遍worker列表,找到第一个空闲的worker,然后将该请求转发给该worker。

master、worker内部通信小技巧

在开发过程中,我们会通过 process.on('message', fn) 来实现进程间通信。

前面提到,master进程、worker进程在server实例的创建过程中,也是通过IPC通道进行通信的。那会不会对我们的开发造成干扰呢?比如,收到一堆其实并不需要关心的消息?

答案肯定是不会?那么是怎么做到的呢?

当发送的消息包含cmd字段,且改字段以NODE_作为前缀,则该消息会被视为内部保留的消息,不会通过message事件抛出,但可以通过监听'internalMessage'捕获。

以worker进程通知master进程创建server实例为例子。worker伪代码如下:

// woker进程
const message = {
 cmd: 'NODE_CLUSTER',
 act: 'queryServer'
};
process.send(message);

master伪代码如下:

worker.process.on('internalMessage', fn);

相关链接

官方文档:https://nodejs.org/api/cluster.html

Node学习笔记:https://github.com/chyingp/nodejs-learning-guide

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Mac地址验证的javascript代码
Nov 09 Javascript
jquery修改属性值实例代码(设置属性值)
Jan 06 Javascript
在Python中使用glob模块查找文件路径的方法
Jun 17 Javascript
AngularJS 执行流程详细介绍
Aug 18 Javascript
js实时获取窗口大小变化的实例代码
Nov 18 Javascript
easyui combogrid实现本地模糊搜索过滤多列
May 13 Javascript
React Native中TabBarIOS的简单使用方法示例
Oct 13 Javascript
element-ui 关于获取select 的label值方法
Aug 24 Javascript
angular4自定义表单控件[(ngModel)]的实现
Nov 23 Javascript
angular4自定义组件非input元素实现ngModel双向数据绑定的方法
Dec 28 Javascript
详解在Angular4中使用ng2-baidu-map的方法
Jun 19 Javascript
详解vue身份认证管理和租户管理
May 25 Vue.js
Node.js进阶之核心模块https入门
May 23 #Javascript
使用 vue-i18n 切换中英文效果
May 23 #Javascript
centos 上快速搭建ghost博客方法分享
May 23 #Javascript
20个最常见的jQuery面试问题及答案
May 23 #jQuery
AngularJS与BootStrap模仿百度分页的示例代码
May 23 #Javascript
jQuery发请求传输中文参数乱码问题的解决方案
May 22 #jQuery
vue的传参方式汇总和router使用技巧
May 22 #Javascript
You might like
雄兵连:第三季确定会出,不过时间未定,鹤熙是第三季的主角!
2020/03/13 国漫
傻瓜化配置PHP环境――Appserv
2006/12/13 PHP
linux下删除7天前日志的代码(php+shell)
2011/01/02 PHP
php ctype函数中文翻译和示例
2014/03/21 PHP
ThinkPHP使用UTFWry地址库进行IP定位实例
2014/04/01 PHP
递归实现php数组转xml的代码分享
2015/05/14 PHP
PHP sleep()函数, usleep()函数
2016/08/25 PHP
Extjs407 getValue()和getRawValue()区别介绍
2013/05/21 Javascript
javascript制作loading动画效果 loading效果
2014/01/14 Javascript
判断一个对象是否为jquery对象的方法
2014/03/12 Javascript
手机图片预览插件photoswipe.js使用总结
2016/08/25 Javascript
axios 封装上传文件的请求方法
2018/09/26 Javascript
jquery中attr、prop、data区别与用法分析
2019/09/25 jQuery
Vue中fragment.js使用方法小结
2020/02/17 Javascript
[40:05]LGD vs Winstrike 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
[01:04:32]DOTA2-DPC中国联赛 正赛 Aster vs LBZS BO3 第二场 2月23日
2021/03/11 DOTA
python在windows下实现ping操作并接收返回信息的方法
2015/03/20 Python
浅谈Django学习migrate和makemigrations的差别
2018/01/18 Python
Python正则匹配判断手机号是否合法的方法
2020/12/09 Python
django fernet fields字段加密实践详解
2019/08/12 Python
解决tensorflow打印tensor有省略号的问题
2020/02/04 Python
Python 解决相对路径问题:&quot;No such file or directory&quot;
2020/06/05 Python
浅谈keras中Dropout在预测过程中是否仍要起作用
2020/07/09 Python
pycharm 实现光标快速移动到括号外或行尾的操作
2021/02/05 Python
HTML5新标签兼容——&gt; 的两种方法
2018/09/12 HTML / CSS
鞋子女王塔玛拉·梅隆同名奢侈品牌:Tamara Mellon
2017/11/22 全球购物
电脑教师的自我评价
2013/12/18 职场文书
如何编写优秀的食品项目创业计划书
2014/01/23 职场文书
社区母亲节活动记录
2014/03/06 职场文书
婚纱店策划方案
2014/05/22 职场文书
习近平在党的群众路线教育实践活动总结大会上的讲话
2014/10/21 职场文书
天那边观后感
2015/06/09 职场文书
紧急迫降观后感
2015/06/15 职场文书
运动会加油稿
2015/07/22 职场文书
敬业奉献模范事迹材料(2016精选版)
2016/02/26 职场文书
nginx刷新页面出现404解决方案(亲测有效)
2022/03/18 Servers