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);
   }) 
}
相关文章
相关标签/搜索