node.js入门 - 7.异常处理与多进程开发
1.异常处理
在我们进行代码开发的时候,异常的捕获处理是一个不能忽略的话题,那么怎么才能捕获到node中的异常呢?或许你最先想到的是try/catch的使用,如下例:
var http = require( 'http' ) var opts = { host: 'sfnsdkfjdsnk.com' , port: 80, path: '/' } try { http.get(opts, function (res) { console.log( 'Will this get called?' ) }) } catch (e) { console.log( 'Will we catch an error?' ) } |
在回答上面代码是否能完成任务之前,我们先做个分析。node采用的是非堵塞的I/O操作,在上面的代码中我调用http.get(),向他传入了一个回调函数,当I/O操作完成的时候回调函数将会被触发。然而,http.get()触发一个回调函数之后就会成功的返回,在执行GET操作过程中的异常是不能被try/catch捕获到的。因为在node中产生了异常向外报告的时候,我们已经离开了javascript的调用堆栈,javascript已经去处理别的事件去了。说的简单点,上面的try/catch是为http.get()准备的,不是为他里面的function(res){}回调函数准备的。然而http.get()是不会发生异常的,他只负责触发function(res){}回调函数,但是回调函数的执行域已经超出了try/catch。
那么有办法捕获到回调函数里面的异常吗?答案是使用异常事件。
var http = require( 'http' ) var opts = { host: 'dskjvnfskcsjsdkcds.net' , port: 80, path: '/' } var req = http.get(opts, function (res) { console.log( 'This will never get called' ) }) req.on( 'error' , function (e) { console.log( 'Got that pesky error trapped' ) }) |
2.多进程开发
在前面的课程中,我们已经了解到node是单线程的,他只使用一个cpu去工作。但是现在多核的处理器那么多,合理的利用多个cpu工作可以大大提高程序的效率,要是不能有效利用服务器资源那不是很可惜。那有没有办法让node也利用服务器上的多个cpu呢,答案是可以使用集群(cluster)技术把多个工作委托给多个子进程去做。node会在其他进程创建当前程序的拷贝。每一个子进程都有些特殊的能力,比如可以和其他进程共享一个socket连接。在使用集群技术的时候,需要创建一个主进程和多个子进程,主进程不负责对具体请求的处理,他只负责工作任务的分配,具体的工作都是交给子进程去处理的。
让我们来看一个例子:
var cluster = require( 'cluster' ); var http = require( 'http' ); var numCPUs = require( 'os' ).cpus().length; if (cluster.isMaster) { // Fork workers. for ( var i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on( 'death' , function (worker) { console.log( 'worker ' + worker.pid + ' died' );<br> cluster.fork(); }); } else { // Worker processes have a http server. http.Server( function (req, res) { res.writeHead(200); res.end( "hello world\n" ); }).listen(8000); } |
通过上面代码,我们使用os模块获取cpu的个数,使用cluster模块创建一个master进程,根据cpu的个数使用cluster.fork()创建相应个数的工作进程,工作进程共享同一个http服务。一个工作进程的错误,不会影响其他工作进程的正常工作。我们使用cluster.on('death', function(worker){},检测工作进程的消亡,一个工作进程消亡以后创建一个全新的工作进程。
在学会使用cluster创建主进程和工作进程之后,我们来学习如何实现他们之间的通信。看下例:
var cluster = require( 'cluster' ); var http = require( 'http' ); var numCPUs = require( 'os' ).cpus().length; var rssWarn = (12 * 1024 * 1024), heapWarn = (10 * 1024 * 1024); if (cluster.isMaster) { for ( var i=0; i<numCPUs; i++) { var worker = cluster.fork(); worker.on( 'message' , function (m) { if (m.memory) { if (m.memory.rss > rssWarn) { console.log( 'Worker ' + m.process + ' using too much memory.' ) } } }) } } else { //Server http.Server( function (req,res) { res.writeHead(200); res.end( 'hello world\n' ) }).listen(8000)<br> //Report stats once a second setInterval( function report(){ process.send({memory: process.memoryUsage(), process: process.pid}); }, 1000) } |
主进程中,使用worker.on('message', function(m) {...})来监听来自工作进程传递的信息。工作进程使用setInterval函数间隔性的调用process.send(),向主进程汇报之间的情况。
如果一些工作进程耗时太长,或者已经没有响应,我们如何关闭它呢,来看下例:
var cluster = require( 'cluster' ); var http = require( 'http' ); var numCPUs = require( 'os' ).cpus().length; var rssWarn = (50 * 1024 * 1024), heapWarn = (50 * 1024 * 1024); var workers = {} if (cluster.isMaster) { for ( var i=0; i<numCPUs; i++) { createWorker() } setInterval( function () { var time = new Date().getTime() for (pid in workers) { if (workers.hasOwnProperty(pid) && workers[pid].lastCb + 5000 < time) { console.log( 'Long running worker ' + pid + ' killed' ) workers[pid].worker.kill() delete workers[pid] createWorker() } } }, 1000) } else { //Server http.Server( function (req,res) { //mess up 1 in 200 reqs if (Math.floor(Math.random() * 200) === 4) { console.log( 'Stopped ' + process.pid + ' from ever finishing' ) while ( true ) { continue } } res.writeHead(200); res.end( 'hello world from ' + process.pid + '\n' ) }).listen(8000)<br> //Report stats once a second setInterval( function report(){ process.send({cmd: "reportMem" , memory: process.memoryUsage(), process: process.pid}) }, 1000) }<br> function createWorker() { var worker = cluster.fork() console.log( 'Created worker: ' + worker.pid) //allow boot time workers[worker.pid] = {worker:worker, lastCb: new Date().getTime()-1000} worker.on( 'message' , function (m) { if (m.cmd === "reportMem" ) { workers[m.process].lastCb = new Date().getTime() if (m.memory.rss > rssWarn) { console.log( 'Worker ' + m.process + ' using too much memory.' ) } } }) } |
答案就是需要我们在主线程中去关闭它。
上面的例子中,我们故意用下面的代码
if (Math.floor(Math.random() * 200) === 4) { console.log( 'Stopped ' + process.pid + ' from ever finishing' ) while ( true ) { continue } } |
让程序有机会堵塞工作进程,这样主进程就有机会得到一个超时的工作进程,从而关闭它了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构