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

父子进程通过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()
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 14,023评论 6 13
  • 第一章 Nginx简介 Nginx是什么 没有听过Nginx?那么一定听过它的“同行”Apache吧!Ngi...
    JokerW阅读 32,923评论 24 1,002
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,088评论 19 139
  • 1 儿子说,今天学校发毕业证 一张小小的初中毕业证虽然说没有什么了不起的 可是,能够顺顺利利拿到这个毕业证也是充满...
    利君理疗阅读 4,037评论 0 0
  • 这个图,我很喜欢,给人的感觉就是:信任!如果你信我,我绝对不让你掉下去!就像我相信你一样,人与人之间最重要的就是彼...
    盈福号阅读 2,948评论 0 0

友情链接更多精彩内容