[Node.js] Cluster,把多核用起来

引子

众所周知,虽然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 吧:

express -e myapp2

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

//CPU几核?
var cpus = require(‘os‘).cpus().length;

//子进程监听消息处理函数
var workerListener = function (msg) {
    if (msg.access)
        console.log(‘user access %s, worker [%d]‘,
                           msg.access, msg.workerid);
};
//fork新的子进程函数
var forkWorker = function(listener){
    var worker = cluster.fork();
    console.log(‘worker [%d] has been created‘,
                                 worker.process.pid);
    worker.on(‘message‘, listener);
    return worker;
};

//Cluster处理
var cluster = require(‘cluster‘);
if (cluster.isMaster) {
    for (var i = 0; i < cpus; i++) {
        forkWorker(workerListener);
    }
} else {
    var app = require(‘./app‘);
    return app.listen(3000);
}

//Cluster收到子进程退出消息
cluster.on(‘exit‘, function (worker, code, signal) {
    console.log(‘worker [%d] died %s, fork a new one.‘,
        worker.process.pid, code || signal);
    forkWorker(workerListener);
});
//Cluster收到子进程运行消息
cluster.on(‘online‘, function(worker){
    console.log(‘worker [%d] is running.‘, worker.process.pid);
});

可以看到:

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

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

var express = require(‘express‘);
var router = express.Router();

/* GET home page. */
router.get(‘/‘, function (req, res) {
    res.render(‘index‘, { title: ‘Express‘,
                              workerid: process.pid });
    process.send({access: ‘/‘, workerid: process.pid});
});

module.exports = router;

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

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel=‘stylesheet‘ href=‘/stylesheets/style.css‘ />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>
    <p>[<%= workerid %>] worker is serving for you.</p>
  </body>
</html>

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

var routes = require(‘./routes/index‘);

app.use(‘/‘, routes);

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

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

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

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


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

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

时间: 2024-08-25 09:48:02

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

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

1 cluster多进程 cluster经过好几代的发展,现在已经比较好使了.利用cluster,可以自动完成子进程worker分配request的事情,就不再需要自己写代码在master进程中robin式给每个worker分配任务了. const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length; if (cluster.isMaster

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

Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹.从在github的访问量超过Rails,到去年底Node.jsS创始人Ryan Dalh加盟Joyent获得企业资助,再到今年发布Windows移植版本,Node.js的前景获得了技术社区的肯定.InfoQ一直在关注Node.js的发展,在今年的两次Qcon大会(北京站和杭州站)都有专门的讲座.为了更好地促进Node.js在国内的技术推广,我们决定开设“深入浅出Node.js”专栏,邀请来自Node.js领域的布道师.

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

1. WEB 服务性能测试和优化 1.1   测试环境搭建 网络环境:内网 压力测试服务器: 服务器系统:Linux 2.6.18 服务器配置:Intel® Xeon™ CPU 3.40GHz 4 CPUS 内存:6GB 反向代理服务器:Nginx服务器 服务器系统:Linux 2.6.18 服务器配置:Pentium® Dual-Core CPU E5800 @ 3.20GHz 2CPUS 内存:2GB 发包服务器: 发包工具:apache 2.2.19自带的ab测试工具 服务器系统:Linu

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

目录 一. 概述 二. 线程与进程 三. cluster模块源码解析 3.1 起步 3.2 入口 3.3 主进程模块master.js 3.4 子进程模块child.js 四. 小结 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 华为云社区地址:[你要的前端打怪升级指南] 一. 概述 cluster模块是node.js中用于实现和管理多进程的模块.常规的node.js应用程序是单线程单进程的,这也意味

【译】 Node.js v0.12的新特性 -- Cluster模式采用Round-Robin负载均衡

原文:https://strongloop.com/strongblog/whats-new-in-node-js-v0-12-cluster-round-robin-load-balancing Node.js v0.12的新特性 -- Cluster采用轮询调度算法来进行负载均衡 November 19, 2013 by Ben Noordhuis 欢迎来到由Node的核心提交者 Ben Noordhuis 和 Bert Belder撰写的系列博文的第一篇.本系列可能由7-8篇构成,主要涵盖

node.js使用cluster实现多进程

首先郑重声明: nodeJS 是一门单线程!异步!非阻塞语言! nodeJS 是一门单线程!异步!非阻塞语言! nodeJS 是一门单线程!异步!非阻塞语言! 重要的事情说3遍. 因为nodeJS天生自带buff, 所以从一出生就受到 万千 粉丝的追捧(俺,也是它的死忠). 但是,傻逼php 竟然嘲笑 我大NodeJS 的性能. 说不稳定,不可靠,只能利用单核CPU. 辣鸡 nodeJS. 艹!艹!艹! 搞mo shi~ 但,大哥就是大哥,nodeJS在v0.8 的时候就已经加入了cluster

Node.js的集群功能以及在Express的配置

Node.js在v0.6.0版本下内置了集群功能,作为cluster模块,用于nodejs的多核处理,也比较容易通过脚本实现一个负载均衡的集群. 脚本参考了其他人的材料,建立一个server.js(因为虚拟机只有1核,为模拟多线程,所以采用numCPUs+4) var cluster = require('cluster'); var http = require('http'); var numCPUs = require('os').cpus().length; if (cluster.is

Node.js的线程和进程

http://www.admin10000.com/document/4196.html 前言 很多Node.js初学者都会有这样的疑惑,Node.js到底是单线程的还是多线程的?通过本章的学习,能够让读者较为清晰的理解Node.js对于单/多线程的关系和支持情况.同时本章还将列举一些让Node.js的web服务器线程阻塞的例子,最后会提供Node.js碰到这类cpu密集型问题的解决方案. 在学习本章之前,读者需要对Node.js有一个初步的认识,熟悉Node.js基本语法.cluster模块.

Node.js和PHP运行机制对比

为什么要用node.js它又有什么优势呢?一个新的技术被大家喜爱那么它就必然有它的优势,那么下面我们就来简单把它和php做一个对比 1 . Node.js 他用的是JavaScript引擎,那么注定它是单线程 ,使用异步方法开辟多个任务,无需像php等待上个任务线程使用结束之后给下个使用,  PHP也是单线程但是它借用Apache服务器提供多线程服务 2 . 高并发,大数据量怎么处理: php : 优化sql ,用组件,用缓存,为了让线程尽快结束,进行下一次任务 node:单线程 .异步.事件驱