在nodejs中建立cluster

在nodejs中建立clusternode

簡介

在前面的文章中,咱們講到了能夠經過worker_threads來建立新的線程,可使用child_process來建立新的子進程。本文將會介紹如何建立nodejs的集羣cluster。服務器

cluster集羣

咱們知道,nodejs的event loop或者說事件響應處理器是單線程的,可是如今的CPU基本上都是多核的,爲了充分利用現代CPU多核的特性,咱們能夠建立cluster,從而使多個子進程來共享同一個服務器端口。socket

也就是說,經過cluster,咱們可使用多個子進程來服務處理同一個端口的請求。oop

先看一個簡單的http server中使用cluster的例子:ui

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} 已啓動`);
}

cluster詳解

cluster模塊源自於lib/cluster.js,咱們能夠經過cluster.fork()來建立子工做進程,用來處理主進程的請求。線程

cluster中的event

cluster繼承自events.EventEmitter,因此cluster能夠發送和接收event。code

cluster支持7中event,分別是disconnect,exit,fork,listening,message,online和setup。server

在講解disconnect以前,咱們先介紹一個概念叫作IPC,IPC的全稱是Inter-Process Communication,也就是進程間通訊。對象

IPC主要用來進行主進程和子進程之間的通訊。一個工做進程在建立後會自動鏈接到它的主進程。 當 'disconnect' 事件被觸發時纔會斷開鏈接。繼承

觸發disconnect事情的緣由有不少,能夠是主動調用worker.disconnect(),也能夠是工做進程退出或者被kill掉。

cluster.on('disconnect', (worker) => {
  console.log(`工做進程 #${worker.id} 已斷開鏈接`);
});

exit事件會在任何一個工做進程關閉的時候觸發。通常用來監測cluster中某一個進程是否異常退出,若是退出的話使用cluster.fork建立新的進程,以保證有足夠多的進程來處理請求。

cluster.on('exit', (worker, code, signal) => {
  console.log('工做進程 %d 關閉 (%s). 重啓中...',
              worker.process.pid, signal || code);
  cluster.fork();
});

fork事件會在調用cluster.fork方法的時候被觸發。

const timeouts = [];
function errorMsg() {
  console.error('鏈接出錯');
}

cluster.on('fork', (worker) => {
  timeouts[worker.id] = setTimeout(errorMsg, 2000);
});

主進程和工做進程的listening事件都會在工做進程調用listen方法的時候觸發。

cluster.on('listening', (worker, address) => {
  console.log(
    `工做進程已鏈接到 ${address.address}:${address.port}`);
});

其中worker表明的是工做線程,而address中包含三個屬性:address、 port 和 addressType。 其中addressType有四個可選值:

  • 4 (TCPv4)
  • 6 (TCPv6)
  • -1 (Unix 域 socket)
  • 'udp4' or 'udp6' (UDP v4 或 v6)

message事件會在主進程收到子進程發送的消息時候觸發。

當主進程生成工做進程時會觸發fork,當工做進程運行時會觸發online。

setupMaster方法被調用的時候,會觸發setup事件。

cluster中的方法

cluster中三個方法,分別是disconnect,fork和setupMaster。

cluster.disconnect([callback])

調用cluster的disconnect方法,實際上會在cluster中的每一個worker中調用disconnect方法。從而斷開worker和主進程的鏈接。

當全部的worker都斷開鏈接以後,會執行callback。

cluster.fork([env])

fork方法,會從主進程中建立新的子進程。其中env是要添加到進程環境變量的鍵值對。

fork將會返回一個cluster.Worker對象,表明工做進程。

最後一個方法是setupMaster:

cluster.setupMaster([settings])

默認狀況下,cluster經過fork方法來建立子進程,可是咱們能夠經過setupMaster來改變這個行爲。經過設置settings變量,咱們能夠改變後面fork子進程的行爲。

咱們看一個setupMaster的例子:

const cluster = require('cluster');
cluster.setupMaster({
  exec: 'worker.js',
  args: ['--use', 'https'],
  silent: true
});
cluster.fork(); // https 工做進程
cluster.setupMaster({
  exec: 'worker.js',
  args: ['--use', 'http']
});
cluster.fork(); // http 工做進程

cluster中的屬性

經過cluster對象,咱們能夠經過isMaster和isWorker來判斷進程是否主進程。

能夠經過worker來獲取當前工做進程對象的引用:

const cluster = require('cluster');

if (cluster.isMaster) {
  console.log('這是主進程');
  cluster.fork();
  cluster.fork();
} else if (cluster.isWorker) {
  console.log(`這是工做進程 #${cluster.worker.id}`);
}

能夠經過workers來遍歷活躍的工做進程對象:

// 遍歷全部工做進程。
function eachWorker(callback) {
  for (const id in cluster.workers) {
    callback(cluster.workers[id]);
  }
}
eachWorker((worker) => {
  worker.send('通知全部工做進程');
});

每一個worker都有一個id編號,用來定位該worker。

cluster中的worker

worker類中包含了關於工做進程的全部的公共的信息和方法。cluster.fork出來的就是worker對象。

worker的事件和cluster的很相似,支持6個事件:disconnect,error,exit,listening,message和online。

worker中包含3個屬性,分別是:id,process和exitedAfterDisconnect。

其中id是worker的惟一標記。

worker中的process,其實是ChildProcess對象,是經過child_process.fork()來建立出來的。

由於在worker中,process屬於全局變量,因此咱們能夠直接在worker中使用process來進行發送消息。

exitedAfterDisconnect表示若是工做進程因爲 .kill() 或 .disconnect() 而退出的話,值就是true。若是是以其餘方式退出的話,返回值就是false。若是工做進程還沒有退出,則爲 undefined。

咱們能夠經過worker.exitedAfterDisconnect 來區分是主動退出仍是被動退出,主進程能夠根據這個值決定是否從新生成工做進程。

cluster.on('exit', (worker, code, signal) => {
  if (worker.exitedAfterDisconnect === true) {
    console.log('這是自發退出,無需擔憂');
  }
});

// 殺死工做進程。
worker.kill();

worker還支持6個方法,分別是:send,kill,destroy,disconnect,isConnected,isDead。

這裏咱們主要講解一下send方法來發送消息:

worker.send(message[, sendHandle[, options]][, callback])

能夠看到send方法和child_process中的send方法參數實際上是很相似的。而本質上,worker.send在主進程中,這會發送消息給特定的工做進程。 至關於 ChildProcess.send()。在工做進程中,這會發送消息給主進程。 至關於 process.send()。

if (cluster.isMaster) {
  const worker = cluster.fork();
  worker.send('你好');

} else if (cluster.isWorker) {
  process.on('message', (msg) => {
    process.send(msg);
  });
}

在上面的例子中,若是是在主進程中,那麼可使用worker.send來發送消息。而在子進程中,則可使用worker中的全局變量process來發送消息。

總結

使用cluster能夠充分使用多核CPU的優點,但願你們在實際的項目中應用起來。

本文做者:flydean程序那些事

本文連接:http://www.flydean.com/nodejs-cluster/

本文來源:flydean的博客

歡迎關注個人公衆號:「程序那些事」最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

相關文章
相關標籤/搜索