原文地址: http://www.moye.me/?p=496

引子

众所周知,虽然Node的底层有一个IO线程池,但其应用层默认是单线程运行的,对于多核CPU环境来说,是一种资源的浪费。

所幸Node提供了child_process 模块,让开发者得以开多个进程,实现每个进程各自利用一个CPU,以实现多核的利用。

child_process 模块给予Node 可以随意创建子进程的能力。因为 child_process 类本身是一个 EventEmitter,所以进程间通信很容易;且父子进程间通信并不通过网络层,而是在内核中完成,高效。

但 child_process 对于开发者来说,编程模型还是过于复杂,需要操心的细节过多,比如:父进程崩溃了,子进程回收是需要开发者提供代码来处理的——如果开发者只是想单纯利用多核模型,对具体工作进程的控制粒度并没有太多设想,那这种开发模型无疑是令人头疼的。

针对这个问题,Node 提供了 Cluster 模块。Cluster 简化了父子模型编程模型,只区分:当前进程是不是 Master,是 Master 就可以fork子进程,不是那就请行使Worker 职责。至于什么资源的回收,负载的调配,uncaughtException的处理,它自有安排。

本质上, Cluster 是 child_process 和 net 模块的组合应用。它不仅简化了编程模型,还使得共享监听同一端口成为可能。

更多关于Cluster的原理这里不表,感兴趣可以移步 → 解读Nodejs多核处理模块cluster

场景

用Node快速搭站呢,当然就用 express 咯  所以,需求就是用 Cluster 做个单机集群,多进程跑 express,提升站点的吞吐量。

实验

先用 express-generator 搭个框子,模板引擎还是使 ejs 吧:

  1. express -e myapp2

express-generator 默认会把 app.js 生成好,一行代码不写,一个完备的 http server 是已经实现的了。So,我们可以实现一个 Cluster 单机集群,clusterMaster.js:

  1. //CPU几核?
  2. var cpus = require('os').cpus().length;
  3.  
  4. //子进程监听消息处理函数
  5. var workerListener = function (msg) {
  6. if (msg.access)
  7. console.log('user access %s, worker [%d]',
  8. msg.access, msg.workerid);
  9. };
  10. //fork新的子进程函数
  11. var forkWorker = function(listener){
  12. var worker = cluster.fork();
  13. console.log('worker [%d] has been created',
  14. worker.process.pid);
  15. worker.on('message', listener);
  16. return worker;
  17. };
  18.  
  19. //Cluster处理
  20. var cluster = require('cluster');
  21. if (cluster.isMaster) {
  22. for (var i = 0; i < cpus; i++) {
  23. forkWorker(workerListener);
  24. }
  25. } else {
  26. var app = require('./app');
  27. return app.listen(3000);
  28. }
  29.  
  30. //Cluster收到子进程退出消息
  31. cluster.on('exit', function (worker, code, signal) {
  32. console.log('worker [%d] died %s, fork a new one.',
  33. worker.process.pid, code || signal);
  34. forkWorker(workerListener);
  35. });
  36. //Cluster收到子进程运行消息
  37. cluster.on('online', function(worker){
  38. console.log('worker [%d] is running.', worker.process.pid);
  39. });

可以看到:

  • Cluster 是通过 isMaster 来区分父子进程的。父进程中处理创建逻辑:根据 CPU的核数,创建相应数量的子进程;子进程中运行具体的 Server 创建代码。够简单的模型~
  • 在 Cluster 父进程端,是始终能获知 当前worker 的。而当前 worker 的 pid 呢,在有worker 句柄情况下,对应的是 woker.process.pid;在子进程运行的上下文中,就是 process.pid。这不难理解~
  • 我们为新fork出来的子进程添加了消息处理函数,使得任何子进程运行代码,都可以随时利用消息触发它:用途挺广泛的,稍后会演示一个例子
  • 在子进程退出时,Cluster 父进程会收到 exit 消息,这时会重fork一个新子进程来补缺

假设用户访问这个站点: localhost:3000/,我希望告诉他,是哪个子进程在为他渲染页面。那么,在 /routes/ 添加一个express 路由 index.js:

  1. var express = require('express');
  2. var router = express.Router();
  3.  
  4. /* GET home page. */
  5. router.get('/', function (req, res) {
  6. res.render('index', { title: 'Express',
  7. workerid: process.pid });
  8. process.send({access: '/', workerid: process.pid});
  9. });
  10.  
  11. module.exports = router;

可以看到,除了路由,还使用 process.send 发送了消息,它实际上会触发之前代码中子进程注册的 workerListener,向它汇报路径和进程号;由于使用了 render,那自然是少不了 ejs 模板, /views/index.js:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title><%= title %></title>
  5. <link rel='stylesheet' href='/stylesheets/style.css' />
  6. </head>
  7. <body>
  8. <h1><%= title %></h1>
  9. <p>Welcome to <%= title %></p>
  10. <p>[<%= workerid %>] worker is serving for you.</p>
  11. </body>
  12. </html>

模板会将 render 携带的子进程 pid 渲染到页面上; 当然少不了在 app.js 中添加路由映射:

  1. var routes = require('./routes/index');
  2.  
  3. app.use('/', routes);

运行程序,在 shell 下 看看进程生成情况:

一父四子,试着用 kill 64163杀死一个子进程。可以在console看到,主进程收到了 exit消息,并重新fork一个新的进程:

测试一下负载,用 Chrome 重复访问几次站点,似乎 worker 子进程 一直未变:

用 ab 来模拟一下并发,让它一个核忙不过来:


换 Safari 浏览器访问一下站点,这次换了一个 worker 进程来服务,速度还不慢 

更多文章请移步我的blog新地址: http://www.moye.me/

[Node.js] Cluster,把多核用起来的更多相关文章

  1. node.js cluster模式启用方式

    众所周知,Node.js运行在Chrome的JavaScript运行时平台上,我们把该平台优雅地称之为V8引擎.不论是V8引擎,还是之后的Node.js,都是以单线程的方式运行的,因此,在多核心处理器 ...

  2. node.js cluster多进程、负载均衡和平滑重启

    1 cluster多进程 cluster经过好几代的发展,现在已经比较好使了.利用cluster,可以自动完成子进程worker分配request的事情,就不再需要自己写代码在master进程中rob ...

  3. 深入浅出Node.js(一):什么是Node.js

    Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹.从在github的访问量超过Rails,到去年底Node.jsS创始人Ryan Dalh加盟Joyent获得企业资助,再到 ...

  4. 读书笔记: 深入浅出node.js

    >> 深入浅出node.js node.js是c++编写的js运行环境 浏览器: 渲染引擎 + js引擎 后端的js运行环境 node.js用google v8引擎,同时提供很多系统级的A ...

  5. 深入浅出Node.js(一):什么是Node.js(转贴)

    以下内容转自:http://www.infoq.com/cn/articles/what-is-nodejs/ 作者:崔康 [编者按]:Node.js从2009年诞生至今,已经发展了两年有余,其成长的 ...

  6. node.js内存缓存的性能情况

    1. WEB 服务性能测试和优化 1.1   测试环境搭建 网络环境:内网 压力测试服务器: 服务器系统:Linux 2.6.18 服务器配置:Intel® Xeon™ CPU 3.40GHz 4 C ...

  7. 深入浅出Node.js(上)

    (一):什么是Node.js Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹.从在github的访问量超过Rails,到去年底Node.jsS创始人Ryan Dalh加盟 ...

  8. Node.js:进程、子进程与cluster多核处理模块

    1.process对象 process对象就是处理与进程相关信息的全局对象,不需要require引用,且是EventEmitter的实例. 获取进程信息 process对象提供了很多的API来获取当前 ...

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

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

随机推荐

  1. 《Windows核心编程》学习笔记(9)– 在win7或者vista系统下提升一个进程的运行权限

    win7或者vista默认运行程序是在受限制的环境下运行的,以减轻病毒对于系统的破坏.那么我们怎样才能提升一个进程的权限以至让它在 管理员模式下运行.当然CreateProcess函数没有提供这个功能 ...

  2. android: 播放音频

    在 Android 中播放音频文件一般都是使用 MediaPlayer 类来实现的,它对多种格式的音 频文件提供了非常全面的控制方法,从而使得播放音乐的工作变得十分简单.下表列出了 MediaPlay ...

  3. Swift入门篇-闭包和函数

    今天主要是给大家分享的是 swift中闭包的用法,我个人觉得闭包就是函数的简写方法,如果您函数不是很熟悉请查阅 swift入门篇-函数 1:函数类型 函数类型 var 变量 :(类型)->返回值 ...

  4. RTP-RTCP-RTSP协议的区别与联系

    一.产生的背景 随着互连网的发展,人们已经不满足于传统的HTTP,FTP和电子邮件等文本信息和服务,而对内容丰富多彩的多媒体信息,服务以及多媒体通信方式提出了需求,包括声音,图象,图形,视频信息等等, ...

  5. Fiddler高级技巧 - 映射路径到本地文件夹

    适用场景: 你是前端开发人员,要开发一个小模块,需要用到线上的环境(账号.数据.跨域等),但你又没有权限往线上传文件 你是移动测试人员,需要将一组接口的返回结果替换为另一组,最简单的办法就是使用Fid ...

  6. 美行Thinkpad八通道快捷入口

    美行Thinkpad八通道快捷入口 链接: http://shop.lenovo.com/perksoffer/us/en/laptops/thinkpad/?__followRobots=true打 ...

  7. 正则指引-字符组demo

    class Program { static void Main(string[] args) { string str = "b"; var result1 = Regex.Is ...

  8. iis7下iis安全狗不能用怎么办(安装iis6兼容性)

    is7下iis安全狗不能用怎么办 | 浏览:126 | 更新:2015-05-14 17:11 1 2 3 4 5 6 分步阅读 windows系统iis安全狗是常用的一款软件,针对网站使用很方便.在 ...

  9. Oracle 数据库表同步方法浅议

    总结一下Oracle数据库表级别的复制同步 一.通过触发器进行表的复制 原理,是监听表上都某一字段进行的DML操作,然后得到DML操作的数据,重新在另一个表上执行DML操作. 优点: 简单,编写一个触 ...

  10. ORA-01654 索引 无法通过 表空间扩展

    "ORA-01654: 索引VGSM.AUDIT_DATA_I无法通过8192(在表空间KLDB中)扩展"   1.有可能是索引表空间不够 select sum(bytes/102 ...