zoukankan      html  css  js  c++  java
  • [Node.js] Cluster,把多核用起来

    原文地址: 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 吧:

    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 下 看看进程生成情况:

    processes of Cluster

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

    fork a new process

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

    access via chrome

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

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

    access site root via safari

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

  • 相关阅读:
    English,The Da Vinci Code, Chapter 23
    python,meatobject
    English,The Da Vinci Code, Chapter 22
    English,The Da Vinci Code, Chapter 21
    English,The Da Vinci Code, Chapter 20
    English,The Da Vinci Code, Chapter 19
    python,xml,ELement Tree
    English,The Da Vinci Code, Chapter 18
    English,The Da Vinci Code, Chapter 17
    English,The Da Vinci Code, Chapter 16
  • 原文地址:https://www.cnblogs.com/moye/p/nodejs_cluster.html
Copyright © 2011-2022 走看看