domain和cluster结合让nodejs更加健壮的运行
来源:互联网 发布:js获取手机app版本号 编辑:程序博客网 时间:2024/06/05 10:49
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
- domain和cluster结合让nodejs更加健壮的运行
- 让代码更加健壮 【总结】
- 让你的C++代码变的更加健壮
- 让你的C++代码变的更加健壮
- 让你的C++代码变的更加健壮
- 让你的C++代码变的更加健壮
- 让你的代码变的更加健壮(Making your C++ code robust)
- 让你的C++代码变的更加健壮(Making your C++ code robust)
- 走出“温室”的花朵更加美丽健壮
- 如何让你的 VB 程序运行的更加高效
- Zipalign优化!让你的手机运行更加流畅!!!
- android怎么让GC运行的更加有效率
- C++中const的用法:使代码更加健壮
- find和grep结合更加方便的查找
- c3p0和QueryRunner的结合使用,让开发更加简便
- c3p0和QueryRunner的结合使用,让开发更加简便
- c3p0和QueryRunner的结合使用,让开发更加简便
- c3p0和QueryRunner的结合使用,让开发更加简便
- tcpdump实用用法
- 最常见的管理误区,你中招了吗?
- Gym 101194F Mr. Panda and Fantastic Beasts
- JavaScript(4)__Js基础<字符串>
- 澳门拥抱阿里云:全面应用智能技术,为城市装上“大脑”
- domain和cluster结合让nodejs更加健壮的运行
- JAVA 导入EXCEL文档
- http简介
- 新手学xingo golang服务器之-golang和unity3d的Protobuf生成(三)
- Java搞定面试中的二叉树题目
- laravel各种路径的获取方法
- Java设计模式 原型模式(Prototype)
- POJ 3714 Raid(分治)
- 剑指offer--树的子结构