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');
    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)
  //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)
  //Report stats once a second   setInterval(function report(){     process.send({cmd: "reportMem", memory: process.memoryUsage(), process: process.pid})   }, 1000) }
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 }
}

讓程序有機會堵塞工作進程,這樣主進程就有機會得到一個超時的工作進程,從而關閉它了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM