美文网首页
9.3 集群稳定之路(《深入浅出nodejs》 笔记)

9.3 集群稳定之路(《深入浅出nodejs》 笔记)

作者: 代小代isDelenDelen | 来源:发表于2017-11-26 17:03 被阅读20次

父子进程通过send()和on('message',callback)分别进行消息的发送和接收处理实现通信机制

自动重启

  1. 在主进程中通过监听子进程的exit事件来或者获知其推出的信息,在多进程架构中加入一些子进程管理的机制,比如重新启动一个进程来继续服务。
  2. 在极端情况下,所有的工作进程都停止接收新的连接,全处在等待退出的状态。但是等到进程完全退出才重启的过程中,所有的新来请求可能存在没有工作进程为新用户服务的情景,这会丢掉大部分请求。 此时需要为退出的流程中新增一个自杀信号(suicide).工作进程在得知要退出时,向主进程发送一个自杀信号,然后才停止接收新的连接,当所有连接断开后才退出。主进程在接收到自杀信号后,立即创建新的工作进程服务。
  3. 对于http服务的长连接,为已有的连接的断开设置一个超时时间是必要的,在限定时间里里强制退出的设置
  4. 对未能捕获的异常进行日志记录
  5. 为了消除无意义的重启,在满足一定规则的限制下,不应当反复重启。比如在单位时间内规定只能重启多少次,超过限制就出发giveup事件,告知放弃重启工作进程这个重要事件。为了完成限量重启的统计,使用队列做标记,并每次重启工作进程之间进行打点并判断重启是否太过频繁。
    giveup事件比uncaughtException更严重的异常,因为giveup事件表示集群中没有任何进程服务了,十分危险,此时应该添加重要日志,并让监控系统监视到这个严重错误,进而报警。
demo:
master.js(主进程,控制和管理子进程)
let fork = require('child_process').fork
let cpus = require('os').cpus()
let server = require('net').createServer()
server.listen(1337)
//限制重启次数
let limit = 10
//限制时间单位
let during = 60000
let restart = []
let isTooFrequently = function () {
  //记录重启时间
  let time = Date.now()
  let length = restart.push(time)
  if (length > limit) {
    // 取出最后10个记录
    restart = restart.slice(limit * -1)
  }
  // 最后一次重启到前10次重启之间的时间间隔
  return restart.length >= limit && restart[restart.length - 1] - restart[0] < during
}

let workers = {}
let createWorker = function () {
  // 检查是否重启的过于频繁
  if (isTooFrequently()) {
    //触发giveup事件,不再重启
    process.emit('giveup', restart.length, during)
    return
  }
  let worker = fork(__dirname + '/worker.js')
  // 处理自杀信号
  worker.on('message', function(message) {
    if (message.act === 'suicide') {
      createWorker()
    }
  })
  //退出时重新启动新的进程
  worker.on('exit', function () {
    console.log('Worker ' +worker.pid + ' exited.')
    delete workers[worker.pid]
    createWorker()
  })
  worker.send('server', server)
  workers[worker.pid] = worker
  console.log('Create worker.pid ' + worker.pid)
}
for(var i =0;i<cpus.length; i++) {
  createWorker()
}
process.on('exit', function() {
  for(let pid in workers) {
    workers[pid].kill()
  }
})
worker.js(各个工作进程)
var http = require('http')
let logger = require('./logger')
let server = http.createServer(function(req, res) {
  res.writeHead(200, {'Content-Type':'text/plain'})
  res.end('handled by child,pid is ' + 'process.pid' + '\n')
  throw new Error('throw exception ' + JSON.stringify(req))
})
let worker;
process.on('message', function(m, tcp) {
  if(m === 'server') {
    worker = tcp
    worker.on('connection', function(socket) {
      server.emit('connection',socket)
    })
  }
})
process.on('uncaughtException', function (err) {
  logger.error(err)
  process.send({act: 'suicide'}) //新增自杀信号
  // 停止接受新的连接
  worker.close(function () {
    // 所有已有连接断开后,推出进程
    process.exit(1)
  })
  // 处理长连接的退出进程机制
  setTimeout(function() {
    process.exit(1)
  }, 5000)
})

负载均衡

  1. NODE默认采取操作系统的抢占式策略(即综合考虑cpu、I/O繁忙度,决定是否进行处理服务)对于不同业务,可能只需要考虑cpu的繁忙度即可
  2. NODE v0.11提供Round-Robin 轮叫调度,其工作方式是由主进程接受连接,将其依次分发给工作进程。分发的策略是在N个工作进程中,每次选择第i = (i+1)mod n 个进程来发送连接。
    在cluster 模块中启用它的方式如下:
cluster.schedulingPolicy = cluster.SCHED_RR (启用Round-Robin)
cluster.schedulingPolicy = cluster.SCHED_NONE(不启用)

或者在环境 变量里设置NODE_CLUSTER_SCHED_POLICY

export NODE_CLUSTER_SCHED_POLICY = rr
export NODE_CLUSTER_SCHED_POLICY = none

状态共享

node不容许在多个进程之间共享数据,但是一些数据 譬如配置数据需要在多个进程之间是一致的。

1. 采用第三方数据存储

eg:redis,然后去轮讯存储的数据

2. 主动通知

创建一个通知进程,去轮询config配置,然后发送通知和查询状态去通知其他进程。推送机制可以按进程间信号传递,在跨多台服务器时会无效,故可以采用Tcp或者UDP的方案

Cluster模块

cluster模块就是child_process 和net模块的组合应用。但是cluster模块应用中,一个主进程只能管理一组工作进程。
暴露事件:

  1. fork:复制一个工作进程后触发该事件
  2. online: 复制好一个工作进程后,工作进程主动发送一条online消息给主进程,主进程收到消息后,触发该事件
  3. listening: 工作进程中调用listen() (共享了服务器Socket)后,发送一条listening消息到主进程,主进程收到消息后,触发该事件
  4. disconnect: 主进程和工作进程之间IPC通道断开后会触发该事件
  5. exit: 有工作进程退出时触发该事件
  6. setup: cluster.setupMaster() 执行后触发该事件
    此时的master.js 只需要写如下代码,此时,需要在worker.js中删除throw new error
let cluster = require('cluster')

cluster.setupMaster({
  exec: "worker.js"
})
let cpus = require('os').cpus()
for(let i =0;i<cpus.length;i++) {
  cluster.fork()
}

相关文章

  • 9.3 集群稳定之路(《深入浅出nodejs》 笔记)

    父子进程通过send()和on('message',callback)分别进行消息的发送和接收处理实现通信机制 自...

  • 常用链接

    html内容转换jade模版 用户脚本(浏览器插件) Tampermonkey 深入浅出Nodejs读书笔记 s...

  • V8垃圾回收机制

    文章地址 本文是深入浅出nodejs的部分学习笔记 V8内存限制 在node中javascript能使用的内存是有...

  • 《深入浅出nodejs》笔记

    第二章:模块机制 总结:nodejs遵循commonjs规范,commonjs定义了模块、I/O、二进制、文件等规...

  • 深入浅出nodejs笔记

    Node.js是一个后端的Javascript运行环境。特点:事件驱动、异步编程nodejs的包结构如下: 一个p...

  • (一)初识NodeJs

    说明:该学习笔记参考《深入浅出Node.js》在学习过程中,添加了自己的理解和适当的补充!仅供参考! NodeJs...

  • (二)NodeJs模块机制和异步I/O

    说明:该学习笔记参考《深入浅出Node.js》在学习过程中,添加了自己的理解和适当的补充!仅供参考! NodeJs...

  • 深入浅出Nodejs之笔记

    深入浅出Nodejs 模块机制 Commonjs规范 node的模块实现步骤:路径分析文件定位编译执行核心模块在n...

  • 2018 第一本书

    阿啦嘞:《深入浅出nodejs》《JS promise迷你书》 不折腾:[ √ ] 《王小波系列》 llp:《皮...

  • 闲聊c/c++ 4: 征服优雅、高效的Libuv库之初识篇

    这一系列文章主要分析nodejs中的核心库Libuv。 我的参考书: 朴灵的深入浅出nodejs Jeffrey ...

网友评论

      本文标题:9.3 集群稳定之路(《深入浅出nodejs》 笔记)

      本文链接:https://www.haomeiwen.com/subject/pqwkbxtx.html