nodejs-第二章-第三節-nodejs多進程-cluster(2-2)

cluster

  • node進行多進程的模塊;
屬性和方法
  1. isMaster屬性,返回是否是主進程,boolean值;
  2. isWorker屬性, 返回該進程是否是工做進程;
  3. fork()方法,只能經過主進程調用,衍生出一個新的worker子進程,返回worker對象;
  4. setupMaster([setting])方法,用於修改fork的默認行爲,一旦調用,將會按照cluster.settings進行設置;
  5. settings屬性;用於配置;
  • 參數exec:worker文件路徑,
  • args:傳遞給worker的參數;
  • execArgv: 傳遞給nodejs可執行文件的參數列表;
const cluster = require('cluster');
const cpuNums = require('os').cpus().length;
const http = require('http');

if (cluster.isMaster) {
  console.log(cpuNums);
  for (var i = 0; i < cpuNums; i++) {
    cluster.fork(); // 至關於node main.js,從新執行本身
    // 和process_child相比,不用從新建立child.js,
  }
  
  cluster.on('fork', worker => {
    console.log(`主進程fork了一個worker,pid爲${worker.process.pid}`)
  })
  
  cluster.on('listening', worker => {
    console.log(`主進程fork了一個worker,pid爲${worker.process.pid}`)
  })
  
  cluster.on('message', data => {
      console.log('主進程接收到了子進程消息爲:',data)
  })
  
  Object.keys(cluster.workers).forEach(item => {
    cluster.workers[item].on('message', data => {
      console.log(data);
    });
  });
  
  cluster.on('disconnect', (worker) => {
    console.log('有工做進程退出了',worker.process.pid)
  })

} else {

  http.createServer((req, res) => {
    res.end('hello')
  }).listen(8001, () =>{
      console.log('child server is runing')
  })    
  
  console.log('我是子進程');
  
  process.send('子說:你好');
}
事件
  1. fork事件,當新的工做進程被fork時觸發,能夠用來記錄工做進程活動;
  2. listening事件,當一個工做進程調用listen()後觸發,事件處理器兩個參數:worker:工做進程對象, address:包含了連接屬性
  • 只有http的服務的listen事件才能觸發此事件
  1. message事件,監聽子進程的消息;當cluster主進程接收任何工做進程發送的消息時觸發;
  • 比較特殊須要在單獨的worker上監聽;
  1. online事件,
  2. disconnect事件,當工做進程斷開時調用;
  3. exit事件,
  4. setup事件,cluster.setupMaster()執行後觸發;

cluster多進程模型

  • 每一個worker進程經過使用cluster.fork()函數,基於IPC(Inter-Process-Communication),實現與master進程間通訊;

那經過child_process.fork()直接建立不就能夠了,爲何要經過clustercss

這種方式只實現了多進程,多進程運行還涉及父子進程通訊,子進程管理,以及負載均衡等問題,這些特性cluster已經作了處理了;html

驚羣現象
  • 多個進程間會競爭一個accept鏈接,產生驚羣現象,效率比較低;
  • 因爲沒法控制一個新的鏈接由哪一個進程來處理,致使worker進程間負載不均衡;

master.jsnode

const net = require('net'); // 是最基礎的網絡模塊,http的基礎就是網絡模塊,最底層是socket
const fork = require('child_process').fork; // 驚羣
var handle = net._createServerHandle('0.0.0.0', 5000); // net模塊建立一個服務,綁定到3000端口,返回一個callback
for (var i = 0; i < 4; i++) {
  console.log('fork', i);
  fork('./worket.js').send({}, handle); // 主進程fork子進程,send信息
}

worker.jsnginx

const net = require('net');
process.on('message', (m, handle) => { // 子進程接收到master信息
  // master接收客戶端的請求,worker去響應
  start(handle);
});

var buf = 'hello nodejs';
var res =
  ['HTTP/1.1 200 OK', 'content-length' + buf.length].join('\r\n') +
  ' \r\n\r\n' +
  buf;

var data = {};

function start(server) {
  // 響應邏輯,重點關注驚羣效果,計數
  server.listen();
  server.onconnection = function(err, hand) {
    var pid = process.pid;
    if (!data[pid]) {
      data[pid] = 0;
    }
    data[pid]++;
    console.log('get a connection on worker,pid = %d', process.pid, data[pid]);
    var socket = net.Socket({
      handle: hand
    });
    socket.readable = socket.writable = true; // 修改socket的讀寫屬性
    socket.end(res);
  };
}
nginx proxy
  • Nginx 是俄羅斯人編寫的十分輕量級的http服務器,是一個高性能的HTTP和反向代理服務器,異步非阻塞I/O,並且可以高併發;
  • 正向代理:客戶端爲代理,服務端不知道代理是誰;
  • 反向代理:服務器爲代理,客戶端不知道代理是誰;
  • nginx的實際應用場景: 比較適合穩定的服務
    • 靜態資源服務器:js,css, html
    • 企業級集羣

守護進程:退出命令窗口以後,服務一直處於運行狀態;算法

cluster多進程調度模型
  • cluster是由master監聽請求,在經過round-robin算法分發給各個worker,避免了驚羣現象的發生;

round-robin 輪詢調度算法的原理是每一次把來自用戶的請求輪流分配給內部中的服務器;服務器

cluster-model.js網絡

const net = require('net');
const fork = require('child_process').fork; // cluster 簡單版本,cluster就是基於child_process去封裝的;

var workers = [];
for (var i = 0; i < 4; i++) {
  workers.push(fork('./child')); // cluster workers
}
var handle = net._createServerHandle('0.0.0.0', 3001); // master
handle.listen();
handle.onconnection = function(err, handle) {
  var worker = workers.pop();
  worker.send({}, handle);
  workers.unshift(worker); // 經過pop 和 unshift實現一個簡單的輪詢
};

child.js併發

const net = require('net');
process.on('message', (m, handle) => {
  debugger;
  start(handle);
});

var buf = 'hello cluster';
var res =
  ['HTTP/1.1 200 OK', 'content-length' + buf.length].join('\r\n') +
  '\r\n\r\n' +
  buf;

function start(handle) {
  console.log('get a worker on server,pid = ' + process.pid);
  var socket = net.Socket({
    handle
  });
  socket.readable = socket.writable = true; // 修改socket的讀寫屬性
  socket.end(res);
}
cluster中的優雅退出
  1. 關閉異常worker進程全部的TCP server(將已有的快速斷開,且再也不接受新的鏈接),斷開和Master的IPC通道,再也不接受新的用戶請求;
  2. Master馬上fork一個新的worker進程,保證總的進程數量不變;
  3. 異常worker等待一段時間,處理完已接受的請求後退出;
if(cluster.isMaster){
        cluster.fork();
    }else {
        // 出錯以後
        try{
            res.end(dddd);  // 報錯,整個線程掛掉,不能提供服務,
        }catch(err){
         // 斷開鏈接,斷開和Master的鏈接,守護進程其實就是重啓;
            process.disconnect(); // or exit()
        }
    }
進程守護
  • Master進程除了接收新的鏈接,分發給各worker處理以外,還像天使同樣默默守護着這些進程,保障應用的穩定性,一旦某個worker進程退出就fork一個新的子進程頂替上去;
  • 這一切cluster模塊已經處理好了,當某個worker進程發生異常退出或者與Master進程失去聯繫(disconnected)時,master進程都會收到相應的事件通知;
cluster.on('exit',function(){
    cluster.fork();
})

cluster.on('disconnect',function(){
    cluster.fork();
})
IPC通訊
  • IPC通訊就是進程間的通訊;
  • 雖然每一個worker進程是相對獨立的,可是他們之間仍是須要通訊的;叫進程間通訊(IPC)通訊;
  • worker和worker之間的通訊經過Master轉發:經過worker的pid
const cluster = require('cluster');
if(cluster.isMaster){
    var worker = cluster.fork();
    worker.send('hi, i am master');
    worker.on('message', (msg) =>{
        console.log(`${msg} is from worker ${worker.id}`)   
    })
}else if(cluster.isWorker){
   process.on('message', (msg) =>{
        process.send(msg);
   }) 
}
相關文章
相關標籤/搜索