设为首页 加入收藏

TOP

【nodejs原理&源码赏析(6)】深度剖析cluster模块源码与node.js多进程(下)(一)
2019-09-17 18:59:00 】 浏览:63
Tags:nodejs 原理 源码 赏析 深度 剖析 cluster 模块 node.js 进程

示例代码托管在:http://www.github.com/dashnowords/blogs

博客园地址:《大史住在大前端》原创博文目录

华为云社区地址:【你要的前端打怪升级指南】

阅读本章需要先阅读本系列前两章内容预热一下。

一. 引言

前两篇博文中已经分别介绍了使用cluster模块建立集群时主进程执行cluster.fork( )方法时的执行逻辑,以及net模块在不同场景下建立通讯的基本原理。本篇继续分析cluster模块,从第一个子进程开始建立服务器讲起,cluster基本用法示例代码再来一遍:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`主进程 ${process.pid} 正在运行`);

  // 衍生工作进程。
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`工作进程 ${worker.process.pid} 已退出`);
  });
} else {
  // 工作进程可以共享任何 TCP 连接。
  // 在本例子中,共享的是 HTTP 服务器。
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('你好世界\n');
  }).listen(8000);

  console.log(`工作进程 ${process.pid} 已启动`);
}

代码是足够精简的,实现过程也确实是很庞大的工程。每一个子进程中执行的逻辑都是http.createServer().listen(),我们来看看它是如何一步一步运作而最终建立通讯机制的,你会发现它和上一节中的简易模型非常相似。

二.server.listen方法

http模块的源码中很容易找到http.createServer( )方法的逻辑就是透传参数生成了一个net.Server实例,这个实例在上一节中就已经介绍过,实际上就只是生成了一个server的实例,所以这里跳转到net.Server.prototype.listen()net.js文件1306-1404行),基本逻辑如下:

Server.prototype.listen = function(...args){
    const normalized = normalizeArgs(args);
    var options = normalized[0];
    /*..获取监听参数中的句柄对象..*/
     options = options._handle || options.handle || options;
    
    //如果options上有句柄,句柄是一个TCP实例
    if(options instanceof TCP){
        //......
        listenInCluster(......);
    }
                        
    //如果配置参数中有fd(file descriptor)
    if(typeof options.fd === 'number' && options.fd >=0){
            //......
        listenInCluster(......);
    }
                        
    //如果参数中有port端口号
    if(typeof options.port === 'number' || typeof options.port === 'string'){
         //.....
         listenInCluster(......);
    }
                         
    //如果参数中有port端口号 或 字符型的pipe名称
    if(typeof options.port === 'number' || typeof options.port === 'string'){
         //.....
         listenInCluster(......);
    }
}

这里不难看出它的逻辑就和net模块官方文档中描述的server.listen( )的几种场景对应,可以监听带有非空handle属性的句柄对象,数字型端口号,字符串型命名管道地址,或者直接传入配置参数合集options,然后分别根据几种不同的情况来调用listenInCluster方法(集群功能的逻辑主线是数字型port,假设传入了12315)。

listenInCluster方法定义如下:

大致可以看出,如果是主进程,就直接调用server._listen2()方法然后return了,否则(也就是在工作进程中的逻辑,敲黑板!!!这里是重点了),构造一个serverQuery的参数集,可以看到里面记录了以各种不同姿势调用这个方法时传入的参数,所以有的参数为null也很正常,然后调用了cluster._getServer( )方法,这就是工作进程在引用cluster模块时引入的child.js中定义并挂载在cluster上的方法,最后一个参数listenOnMasterHandle是一个回调函数,也是一个错误前置风格的函数,可以看到,它接收了一个句柄对象,并把这个句柄对象挂载在了子进程这个server实例的_handle属性上,接着也调用了server._listen2( )方法,可以看到两种情况下调用这个方法时传入的参数是一样的。接着来到server._listen2( )方法,它绑定了setupListenHandle方法(别抓狂,这是net模块中相关逻辑的最后一步了),简化代码如下:

function setupListenHandle(......){
  if (this._handle) {
    //工作进程在执行上一步逻辑时,在cluster._getServer()回调函数中把一个handle传递给了server._handle
    debug('setupListenHandle: have a handle already');
  } else {
    //主进程会执行的逻辑
    debug('setupListenHandle: create a handle');
      //......
       rval = createServerHandle(address, port, addressType, fd, flags);
      //......
      this._handle = rval;
}
  //......
  this._handle.onconnection = onconnection;
  this._handle[owner_symbol] = this;
  //....
}

工作进程通过cluster._getServer( )方法拿到了一个handle,所以不会再生成,而主进程server.listen(port)执行时会走到else分支,然后生成一个新的绑定了端口号的特殊

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇JavaScript的二维数组 下一篇js 加密混淆工具

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目