Eggjs中的进程管理

Cluster

javscript的代码只能运行在单线程中,也就是一个nodejs进程只能运行在一个cpu上。若是须要充分利用多核cpu的并发优点,可使用cluster模块。cluster可以建立多个子进程,每一个进程都运行同一份代码,而且监听的是同一个端口。html

简单利用Cluster fork cpu个数子进程的代码以下:node

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

if (cluster.isMaster) {
  // 若是是Master则进行fork操做,启动其余进程
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
  // 不然启动http服务监听
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}
  • 为何cluster fork多份源码跑在多个子进程上没有报端口被占用?
    cluster模块会hack掉worker中的监听,端口仅由master的TCP服务监听了一次segmentfault

  • Master是如何处理请求转发的worker的?
    全部请求会统一通过内部TCP服务,符合必定负载均衡的挑选出一个worker并发送内部消息,该worker接收到消息后执行具体业务逻辑。(除 Windows 以外全部平台上的默认方法是循环方法,接受新的链接,并以循环方式将它们分发给各个工做线程,同时使用一些内置的智能,来实现负载均衡。)

EGG框架中的多进程

Agent机制

在eggjs中,除了有worker还有Agent,实际上也是一个worker,为了区别把他们称为agent worker和app worker。
Agent worker的做用是用来处理一些后台运行逻辑,好比说打印日志,不须要在4个app worker上都去执行,不对外提供服务,只处理公共事务,因此稳定性相对来讲是很高的。并发

+--------+          +-------+
                | Master |<-------->| Agent |
                +--------+          +-------+
                ^   ^    ^
               /    |     \
             /      |       \
           /        |         \
         v          v          v
+----------+   +----------+   +----------+
| Worker 1 |   | Worker 2 |   | Worker 3 |
+----------+   +----------+   +----------+

Master-Agent-Worker模型下,master承担了相似于pm2的进程管理的职责,可以完成worker的初始化/重启等工做。
image.pngapp

进程守护

异常能够简单分为两类,第一类是能够监听process.on('uncaughtException', handler)捕获的异常,经过监听事件可使得进程不会异常推出还有机会能够继续执行。第二类是被系统杀死直接推出的异常。
eggjs使用了graceful和egg-cluster让异常发生时master可以马上fork出一个新的worker保持链接的worker数。负载均衡

egg-cluster

进程启动顺序框架

+---------+           +---------+          +---------+
|  Master |           |  Agent  |          |  Worker |
+---------+           +----+----+          +----+----+
     |      fork agent     |                    |
     +-------------------->|                    |
     |      agent ready    |                    |
     |<--------------------+                    |
     |                     |     fork worker    |
     +----------------------------------------->|
     |     worker ready    |                    |
     |<-----------------------------------------+
     |      Egg ready      |                    |
     +-------------------->|                    |
     |      Egg ready      |                    |
     +----------------------------------------->|
  1. Master 启动后先 fork Agent 进程,同时监听'agent-exit, agent-start'事件,agent 启动成功后发送agent-start事件(IPC进程间通讯)通知masterui

    • master监听到agent-exist事件会在一秒后再fork一次agent worker,保持agent稳定onAgentExit
    • agent-start事件为once,即便重启了agent app worker也不受影响
  2. Master收到 agent-start 通知fork多个App Worker,这里的fork用的是cfork包,负责 worker 的启动,状态监听以及 refork 操做,保证worker的数量
  3. 多个App worker 启动成功后发送app-start事件通知到master
  4. 全部的进程初始化成功后,Master 通知 Agent 和 Worker 应用启动成功

IPC进程通讯

在nodejs中实现进程通讯能够经过监听messgae事件实现this

'use strict';
const cluster = require('cluster');

if (cluster.isMaster) {
  const worker = cluster.fork();
  worker.send('hi there');
  worker.on('message', msg => {
    console.log(`msg: ${msg} from worker#${worker.id}`);
  });
} else if (cluster.isWorker) {
  process.on('message', (msg) => {
    process.send(msg);
  });
}

在eggjs Agent机制中,agent也是也给worker,因此IPC通道存在与master和agent/app worker之间,而agent和app worker之间的通讯须要经过master转发。
Eggjs包装了Message类,用from to标记涞源和去向。spa

this.messenger.send({
      action: 'agent-exit',
      data: { code, signal },
      to: 'master',
      from: 'agent',
    });

参考连接

https://juejin.im/entry/59bcce1b5188257e82676b53
https://zhuanlan.zhihu.com/p/49276061
http://www.javashuo.com/article/p-hljxobfr-db.html
https://eggjs.org/zh-cn/core/cluster-and-ipc.html

相关文章
相关标签/搜索