domain和cluster结合让nodejs更加健壮的运行

nodejs的特性就是单线程,异步I/O,这个特性就决定了异常处理会是nodejs比较棘手的问题,在日常的开发中经常会出现由于程序的bug导致整个web应用瘫痪。如果这种异常不处理好的话,那使用nodejs的web应用没人敢把他部署到线上。
nodejs常见的异常处理方式有三种try catch,uncaughtException,domain,这里我不详细介绍三种方式。度娘上一搜大把大把的。这里主要介绍我自己结合cluster和domain做的一个针对express的异常处理模块。
try catch的缺点是不能捕获异步代码块的异常。
uncaughtException的缺点是可能会导致上下文丢失。内存方面可能会出现问题。
上面两种虽然有缺点,但是也是可以使用的,可以达到辅助治疗的作用,但是如果你要靠他们来解决整个项目的异常,反正臣妾是做不到。在、这个时候domain在就是一个更好的解决方案了。domain是nodejs在后来的版本中新增的一个自带模块,怎么使用自己去看api,作为一个有追求的程序员一定要学会看api,虽然我也不喜欢看,但是我是一个有理想有抱负的马龙,所以我努力的去看……。
其实nodejs官方提倡当异常出现的时候,最好的解决方式就是重启应用,重启???,重启后我的会话不就没了吗,我之前的登陆信息不是没了吗?我……。如果你还在纠结这个问题,那么你就out了,说实话,现在还在使用session来保存用户登陆信息的应用,基本上只剩下一些传统行业了。大多数的应用都是将用户的登陆信息以及鉴权信息保存在缓存数据库中,什么redis,mongodb。这么做的原因便于做集群。传统的session,要做集群的话,需要借助容器来实现,比如websphere,他自带集群部署。但是用过WAS的人都知道,东西好用,但是进口货(IBM),贵啊。再说,我们都用nodejs来搭建后台服务了,要是不用缓存数据库是不是太不伦不类了,…格是不是太低了。所以,我得出结论就是,当出现异常时大胆的重启,在重启前我们只要做好日志记录工作。便于我们事后分析原因。
nodejs是单线程的,这会导致一个什么问题呢,就是cpu的利用率低,不管你的计算机是双核,四核的还是1000核的。他都只能同时使用1个核,其他的只能处于空闲状态。nodej的开发者也注意到这样做实在太暴殄天物了,于是在nodejs中自带了cluster(集群)模块,这里集群和我们常见的集群概念有点不一样,一般的集群的程序是分布在不同的机器上。但是这里的cluster提供的集群是在同一台机器上做集群,他的作用就是充分的利用服务器的cpu,我们可以根据服务器的cpu数来fork多个server进程,这些进程可以共享端口。每当请求来的时候,我们也不知到是哪个进程去处理的这个请求,系统会根基目前进程的空闲情况自动分配,这在一定的程度上起到了负载均衡的作用。那这和我今天要说的异常处理有什么关系呢?下面我就不卖关子了,直接贴代码:

let cluster = require("cluster");
let cpus = require("os").cpus().length;
let domain = require("domain");
module.exports = {
    startServer:function(fun){
        if(cluster.isMaster){
            for(let i = 0;i<cpus;i++){
                cluster.fork();
            }
        }else if(cluster.isWorker){
            fun(cluster);
        };
        cluster.on("fork",(worker)=>{
            console.info('%s %s',new Date(),`worker${worker.process.pid}进程启动成功`)
        });
        cluster.on("exit",(worker,code,signal)=>{
            console.info('%s %s',new Date(),`worker${worker.process.pid}进程退出`);
            for(let id in cluster.workers){
                console.info('%s %s',new Date(),`目前还有worker${worker.process.pid}在工作`);
            }
            if(worker.isDead){
                console.info('%s %s',new Date(),"新进程将在1s后启动");
                setTimeout(()=>{
                    cluster.fork();
                },1000)
            }
        })
    },
    clusterDomain:function(options){
        if(!options.killTimeout){
            options.killTimeout = 0;
        };
        if(!options.error){
            options.error = function(res){
                res.send("error request!")
            }
        }
        return function(req,res,next){
            let d = domain.create();
            d.add(req);
            d.add(res);
            d.on("error",(err)=>{
                d._throwErrorCount = (d._throwErrorCount || 0)+1;
                if(d._throwErrorCount>1){
                    console.error('[express-cluster-domain] %s %s throw error %d times',req.method,req.url,d._throwErrorCount);
                    console.log(err);
                    return;
                }
                next(err);
                let killtimer = setTimeout(()=>{
                    console.info('%s [worker:%s] will be killed',new Date(),process.pid);
                    process.exit(1);
                },options.killTimeout);
                if(cluster.worker){
                   try{
                     console.error('%s [worker:%s] will  disconnect',new Date(),process.pid);
                      cluster.worker.disconnect();
                   }catch(e){
                    //TODO handle the exception
                      console.error('%s [worker:%s] throw Error\n%s',new Date(),process.pid,e.stack);
                   }
                };
                if(options.error){
                    console.log("-------------------------")
                    options.error(res)
                }
            });
            d.run(next);
        }
    }
}

startServer 的作用是根据cpu的数量决定起几个进程。然后监听进程退出的事件,然后重启进程。为什么要这么做呢,应为使用cluster一方面可以充分利用服务器的多核处理器,一方面是可以保证当异常出现,进程退出的时候还有其他的进程可以继续提供服务,因为重启也是需要时间的。那如果说请求多,所有的进程都挂了呢?而新的进程也都还没有来得及重启。那这就是严重bug了。这时测试的价值就体现出来了。
clusterDomain的作用的就是将所有的请求和响应加入到domain中,其实就是domain的常规操作, 不知道domainz怎么用的还是那句话,看API文档。在domain中当异常捕获的时候,记录日志,然后进程退出。这个时候上面startServer中的进程退出监听就会收到进程退出的事件,然后按计划重启进程。
其实整个模块的实现思路是比较简单明了的,就是将cluster和doimain结合。一个负责启动进程,监听进程,另外一个负责捕获异常,进程退出。
上面的介绍中可能会大量出现”通假字”,请忽略,如果有改进的地方请提点,谢谢!。
模块github地址:https://github.com/839305939wang/express-cluster-domain.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值