zoukankan      html  css  js  c++  java
  • node.js cluster多进程、负载均衡和平滑重启

    1 cluster多进程

    cluster经过好几代的发展,现在已经比较好使了。利用cluster,可以自动完成子进程worker分配request的事情,就不再需要自己写代码在master进程中robin式给每个worker分配任务了。

    const cluster = require('cluster');
    const http = require('http');
    const numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
      // Fork workers.
      for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
      }
    
      cluster.on('exit', (worker, code, signal) => {
        console.log(`worker ${worker.process.pid} died`);
      });
    } else {
      // Workers can share any TCP connection
      // In this case it is an HTTP server
      http.createServer((req, res) => {
        res.writeHead(200);
        res.end('hello world
    ');
      }).listen(80);
    }

    上述简单的代码,就实现了根据CPU个数,创建多个worker。至于实际项目中,是正好一个核对应一个worker呢,还是一个核对应2、3个worker呢,就视情况而定了。如果项目中,等待其他服务器(例如数据库)响应特别长时间,设置2个以上worker应该会更好。

    不过一般而言,一个CPU对一个worker就挺好的了。

    那么,整个架构就类似这样:

    image

    Master进程,需要做的就是监控worker的生命周期,如果发现worker挂掉了,就重启worker,并做好相应的log。

    整个架构没有太大的难点,重点就是做好一些细节处理,例如重启、日志、5秒心跳包等。

    多进程的架构,相对原始的单进程+pm2重启好处肯定多很多,整个node服务会更稳定,不会突然彻底挂了。

    另外,对比pm2多进程,也有优势,主要是master的逻辑掌握在开发自己手中,可以做好自定义的log和邮件、短信告警。

    为了整个nodejs服务管理方便,在master进程中,我们一般开启管理端口的监听,例如12701,通过命令行curl 127.0.0.1:12701:xxx发起一个简单的http get请求,轻松管理。

    例如xxx传入reload,可以作为服务器重启的指令。

    2 负载均衡

    说到多进程,目的肯定是尽可能利用多核CPU,提高单机的负载能力。

    但往往在实际项目中,受到业务逻辑的处理时间长短和系统CPU调度影响,导致实际上所有进程的负载并不是理想的彻底均衡。

    官方也说了:

    In practice however, distribution tends to be very unbalanced due to operating system scheduler vagaries. Loads have been observed where over 70% of all connections ended up in just two processes, out of a total of eight.

    翻译一下:70%的请求最终都落到2个worker身上,而这2个worker占用更多的CPU资源。

    那么在实际项目部署,我们可以尝试更进一步的措施:绑定CPU。4核CPU,我们fork出4个worker,每个worker分别绑定到#1-#4 CPU。

    node并没有给我们提供现成的接口,不过我们可以使用linux的命令:taskset

    在node中,我们可以使用child_process执行shell。

    cp.exec('taskset -cp ' + (cpu) + ' ' + process.pid,{ 
        timeout: 5000 
    },function(err,data,errData){ 
        if(err){ 
            logger.error(err.stack); 
        } 
        
        if(data.length){ 
            logger.info('
    ' + data.toString('UTF-8')); 
        } 
        
        if(errData.length){ 
            logger.error('
    ' + errData.toString('UTF-8')); 
        } 
    });

    按实际情况来看,效果是不错的。

    imageimageimageimage

    3 平滑重启

    每次发布新版本,服务器必然需要重启。

    简单粗暴的,杀掉主进程,全部重启,必然会有一段时间的服务中断。

    image

    对于小企业还好,可以安排在凌晨重启,但对于大公司大产品来说,就不能这么粗暴了。

    那么我们需要平滑重启,实现重启过程中,服务不中断。

    策略并不复杂,但非常有效:

    1、worker进程轮流重启,间隔时间;

    2、worker进程并不是直接重启,而是先关闭新请求监听,等当前请求都返回了,再重启。

      try {
            // make sure we close down within 30 seconds
            var killtimer = setTimeout(() => {
              process.exit(1);
            }, 30000);
    
            // stop taking new requests.
            server.close();
    
            // Let the master know we're dead.  This will trigger a
            // 'disconnect' in the cluster master, and then it will fork
            // a new worker.
            cluster.worker.disconnect();
    
          } catch (er2) {
          }

    实施了平滑重启后,服务器的吞吐率会平滑很多。

    image

  • 相关阅读:
    如何利用JMeter模拟超过 5 万的并发用户
    JQUERY插件JqueryAjaxFileUplaoder----更简单的异步文件上传
    jmeter内存溢出处理方式记录
    JMeter压力测试入门教程[图文]
    本机Tomcat启动myeclipse,用Jmeter录制脚本端口冲突解决办法
    Jmeter 录制脚本
    Jmeter基础之---jmeter基础概念
    Jmeter性能测试 入门
    SSH Secure Shell Client安装和使用
    面向对象2
  • 原文地址:https://www.cnblogs.com/kenkofox/p/5431643.html
Copyright © 2011-2022 走看看