美文网首页
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》 笔记)

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