node服务器的研究

node服务器集群

这边文章主要讨论是node服务器放面的研究,这里做一些要点学习笔记

1. cluster

通过对cluster的研究,发现node-cluster只负责创建node进程,以及进程间的通信。

The cluster module supports two methods of distributing incoming connections.

  1. The first one (and the default one on all platforms except Windows), is the round-robin approach, where the master process listens on a port, accepts new connections and distributes them across the workers in a round-robin fashion, with some built-in smarts to avoid overloading a worker process.
  2. The second approach is where the master process creates the listen socket and sends it to interested workers. The workers then accept incoming connections directly.
    来源:https://nodejs.org/dist/latest-v5.x/docs/api/cluster.html#cluster_how_it_works

cluster支持两种负载均衡:

  1. master监听端口,当请求来临时,master决定调用某个worker实现处理业务
  2. master创建多个worker,多个worker监听同一个端口,当请求来临时由系统默认的调度算法选择某个worker进程处理业务。但是这种存在一个问题:系统往往考虑到进程上下文切换的问题,会将多个请求一次发给一个空闲worker进程

tips:

  1. linux2.6已经修复惊群问题
  2. 多个进程之间是可以同时监听一个socket端口的,这属于端口复用

参考文章

  1. Node.js V0.12新特性之Cluster轮转法负载均衡:http://www.infoq.com/cn/articles/nodejs-cluster-round-robin-load-balancing

2. pm2

1. pm2log:pm2对于log的实现:

在源码Utility.js中,可以看到pm2重写了console的方法,并将其捕获这数据以EventEmitter-log:PM2的形式提交出去。当log:PM2事件被处理的时候,console打出的日志就会根据目前的输出策略,输出到某一个日志文档中。

overrideConsole : function(bus) {
    //...
    //重写console的方法
    var hacks = ['info', 'log', 'error', 'warn'], consoled = {};
    hacks.forEach(function(method){
        consoled[method] = console[method];
    });

    hacks.forEach(function(k){
        console[k] = function(){
            if (bus) {
                //发送log事件
                bus.emit('log:PM2', {
                    process : {
                        pm_id      : 'PM2',
                        name       : 'PM2',
                        rev        : null
                    },
                    at  : Utility.getDate(),
                    data : util.format.apply(this, arguments) + '\n'
                });
            }
            // do not destroy variable insertion
            arguments[0] && (arguments[0] = timestamp() + arguments[0]);
            consoled[k].apply(console, arguments);
            };
        });
    }
}

//处理log事件
bus.on('log:*', function(type, data) {
    //....
    process.stdout.write(util.format(line) + '\n');
});

2. pm2 watch: pm2对于watch&restart的实现。

  1. 先说一下文件变化的监听,node基本库filesystem.watch是可以监听整个目录的变化的。而pm2适用的是chokidarnode包进行监听。这里监听功能可以看出原理。
  2. restart的机制:

    const cluster = require('cluster');
    cluster.setupMaster({
      exec: 'worker.js'
    });
    cluster.fork(); // https workerA  步A
    //修改worker.js
    cluster.fork(); // https workerB  步B
    

    说一下fork的实现,每次执行cluster.fork都相当于重新读一遍worker.js这个文件,然后执行。在上面的例子中,如果在步A步B两个步骤之间,修改了worker.js,那么启动的两个workerA和workerB的执行是不一样的。

  3. pm2对于进程重启就是使用node基本库process.kill去杀死一个进程,杀死成功之后,然后在执行一遍cluster.fork,那么此时最新修改的就生效了。

3. koa

1. koa middleware:koa中间件的实现

koa中间件的前提:所有的中间件,除了最后一个,都要带有next参数,且必须有yield next;

koa中间件实现的原理:

  1. 把所有的app.use(generator)中的generator放入到一个数组里面。
  2. 使用koa-compose把中间件数组,组成一个函数。这个函数的结果是每一个中间件的嵌套型结构(即第一个中间件对象有一步next的value指向第二个中间件对象,第二个中间件对象有一步next的value指向第三个中间件对象,如下图)。

    module.exports = compose;
    
    function compose(middleware){
        return function *(next){
            if (!next) next = noop();
            var i = middleware.length;
            while (i--) {
                next = middleware[i].call(this, next);
            }
            yield *next;
        }
    }
    function *noop(){}
    
  3. 使用co组件,有两个作用:第一个:将第二步生成返回的嵌套型结构逐步解析,遍历每一个中间件对象;将第二部的函数封装成Promise。

4. 其他

1. [Node] - 16年,新 Node 项目注意点

  1. https://github.com/gf-rd/blog/issues/29
  2. Web 应用开发的十二条军规:有待继续深入研究。