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

相關文章
相關標籤/搜索