Eggjs学习系列(七) 多进程实践

本文详细介绍了Node.js的Cluster模块,用于在多核环境下提升项目性能。Eggjs在此基础上构建了多进程模型,包括错误处理、Agent机制以及进程间通讯。文中特别强调了Agent的作用,它是处理公共事务的独立进程,增强了系统的健壮性。此外,还讨论了负载均衡的Sticky Mode,确保请求被定向到相同的Worker,解决了Session问题。最后,探讨了Eggjs如何通过建立Agent和Worker的直接连接,优化了多进程研发模式。

JavaScript 是单线程的,只能运行在一个CPU上,这样不能充分发挥计算机的性能。为了更好地利用多核环境,Node.js 提供了 Cluster 模块,可以方便的创建多个子进程,提高项目运行效率。

Cluster 模块

Cluster 模块将进程分为 Master 进程和 Worker 进程:

  • 负责启动其他进程的叫做 Master 进程,只负责启动其他进程。
  • 其他被启动的叫 Worker 进程,接收请求,对外提供服务。
  • Worker 进程的数量一般根据服务器的 CPU 核数来定,这样就可以完美利用多核资源。

范例代码:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
// 判断是不是 Master 进程
if (cluster.isMaster) {
   
   
  // 创建子进程
  for (let i = 0; i < numCPUs; i++) {
   
   
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
   
   
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
   
   
  // Worker 进程能够共用TCP连接
  http.createServer(function(req, res) {
   
   
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}

Cluster 原理

  1. Master、Worker进程通信

    master 进程通过 cluster.fork() 来创建 worker进程。cluster.fork() 内部 是通过 child_process.fork()来创建子进程。所以 master进程、worker进程是父、子进程的关系。Master 进程、Woker进程之间通过IPC通道进行通信。

  2. 实现端口共享

    net模块中,对 listen() 方法进行了特殊处理。如果当前进程是 Master 进程则不做处理,如果是 Worker 进程则创建server实例。然后通过IPC通道,向 Master 进程发送消息,**让 Master 进程也创建 server 实例,并在该端口上监听请求。**当请求进来时,master进程将请求转发给worker进程的server实例。

    // lib/net.js 源代码
    function listen(self, address, port, addressType, backlog, fd, exclusive) {
         
         
      exclusive = !!exclusive;
      // 引入 cluster 模块
      if (!cluster) cluster = require('cluster');
      // 判断当前进程是不是 Master 进程
      if (cluster.isMaster || exclusive) {
         
         
        self._listen2(address, port, addressType, backlog, fd);
        return;
      }
      // _getServer 向 Master 进程注册该 Worker,若master进程是第一次接收到监听此端口/描述符下的Worker,则起一个内部TCP服务器,来承担监听该端口/描述符的职责,随后在master中记录下该worker。
      // Hack掉worker进程中的net.Server实例的listen方法,使其不再监听接口。
      cluster._getServer(self, {
         
         
        address: address,
        port: port,
        addressType: addressType,
        fd: fd,
        flags: 0
      }, cb);
    
      function cb(err, handle) {
         
         
        // ...
        self._handle = handle;
        self._listen2(address, port, addressType, backlog, fd);
      }
    }
    
  3. 分发多个请求到worker

    每当worker进程创建server实例来监听请求,都会通过IPC通道,在master上进行注册。当客户端请求到达,master会负责将请求转发给对应的worker。

    具体转发给哪个worker?这是由转发策略决定的。可以通过环境变量NODE_CLUSTER_SCHED_POLICY设置,也可以在cluster.setupMaster(options)时传入。

    默认的转发策略是轮询(SCHED_RR):当有客户请求到达,master会轮询一遍worker列表,找到第一个空闲的worker,然后将该请求转发给该worker。

  4. 发送内部消息

    当发送的消息包含cmd字段,且改字段以NODE_作为前缀,则该消息会被视为内部保留的消息,不会通过message事件抛出,但可以通过监听’internalMessage’捕获。

    // woker进程
    const message = {
         
         
      cmd: 'NODE_CLUSTER',
      act: 'queryServer'
    };
    process.send(message);
    // master进程接收方式
    worker.process.on('internalMessage', fn);
    

Eggjs 多进程模型

错误处理

在多进程的情况下,一个重要的要求就是保证程序出错的时候还能够正常运行,这就叫健壮性(又叫鲁棒性)。Eggjs 框架在这方面也提供了相关的保证。

Node.js 进程退出的两大情况

  • 未捕获异常 Node.js 提供了 process.on('uncaughtException', handler) 接口来捕获未被处理的异常。Eggjs 则采用关闭异常 Worker 进程所有的 TCP Server, Master 进程立刻 fork 一个新的 Worker 进程来保证服务正常进行。异常 Worker 在处理完请求后退出
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值