Node.js之错误处理

Node.js之错误处理

1. 使用 domain 模块处理错误

try..catch
多用于捕捉同步方法中的抛出错误,但不能用try..catch捕捉异步方法中抛出de错误
如:

  1 var http = require('http')
  2 try{
  3 http.createServer(function(req,res){
  4 if(req.url!="/favicon.ico"){
  5 noneexist();//不存在本函数
  6 res.writeHead(200,{'Content-Type':'text/html'})
  7 res.write('<head><meta charset="utf-8"/></head>')
  8 res.end('你好\n')
  9 }
 10 }).listen(8000,"127.0.0.1")
 11 }
 12 catch(err){
 13 console.log('接收客户端请求时发生以下错误:')
 14 console.log(err.code)
 15 }

在用浏览器请求时,会出错,因为noneexist()不存在,程序也会停止运行

$ node app.js
D:\Users\yuan\Desktop\app.js:6
noneexist();//不存在本函数
^
ReferenceError: noneexist is not defined
at Server.<anonymous> (D:\Users\yuan\Desktop\app.js:6:13)
at emitTwo (events.js:125:13)
at Server.emit (events.js:213:7)
at parserOnIncoming (_http_server.js:602:12)
at HTTPParser.parserOnHeadersComplete (_http_common.js:116:23)

为了防止程序被强制关闭,提供了一个uncaughtException事件,用于捕捉及处理任何未被处理的错误
可将代码改成如下形式:

 var http = require('http')
 var http = require('http')
 http.createServer(function(req,res){
    if(req.url!="/favicon.ico"){
        noneexist();//不存在本函数
        res.writeHead(200,{'Content-Type':'text/html'})
        res.write('<head><meta charset="utf-8"/></head>')
        res.end('你好\n')
      }
 }).listen(8000,"127.0.0.1")
process.on('uncaughtException',function(err){
console.log('接收客户端请求时发生以下错误:')
console.log(err)
})

运行结果如下会报错,但不会停止:

$ node app.js
接收客户端请求时发生以下错误:
ReferenceError: noneexist is not defined
at Server.<anonymous> (D:\Users\yuan\Desktop\app.js:6:13)
at emitTwo (events.js:125:13)
at Server.emit (events.js:213:7)
at parserOnIncoming (_http_server.js:602:12)
at HTTPParser.parserOnHeadersComplete (_http_common.js:116:23)
|

但这种方法可能会出现资源泄露,因此利用domain模块进行处理错误,代码如下:

var http = require('http')
var domain = require('domain')  
 http.createServer(function(req,res){
    var d = domain.create()
   	        d.once('error',function(err){
            res.writeHead(200,{'Content-Type':'text/html'})
	     	res.write('<head><meta charset="utf-8"/></head>')
		   	res.write('服务器接收客户端请求时发生以下错误: ')
        	res.end(err.message)
   	    })	
     d.run(function(){
     	if(req.url!=="/favicon.ico"){
		nonexist();
		res.writeHead(200,{'Content-Type':'text/html'})
		res.write('<head><meta charset="utf-8"/></head>')
		res.end('你好\n')
	}
     })
      
 }).listen(8000,"127.0.0.1")

浏览器访问后在浏览器中会出现错误信息,程序不会间断
下面详细介绍Domain

2. 创建并使用Domain

  1. 创建domain对象:

    var domain= domain.create();

  2. 当该对象捕获到任何错误信息时,触发该对象的error事件,可以通过对监听对象的error事件并指定事件回调函数的方法来实现对捕捉到错误时的处理。

     domain.on('error',function(err){
     	//事件回调函数代码
     })
    
  3. 在domain对象中定义了一个name属性值,用于获取Domain对象的名字

    domain。name

  4. Domain对象被创建后,利用run方法指定Domain监视对象

     domain。run(fn)//fn为一函数
    

eg:

var domain = require('domain')
var fs = require('fs')
var d = domain.create()
d.name = 'dl'
d.on('error',function(err){
	console.error(d.name,err)
})
d.run(function(){
	process.nextTick(function(){
		setTimeout(function(){
			fs.open('non-existent file','r',function(err,fd){
			if(err)throw err
			})
		},1000)
	})
})

输出结果为:

 $ node app.js
 dl { Error: ENOENT: no such file or directory, open 'D:\Users\yuan\Desktop \non-existent file'
 errno: -4058,
 code: 'ENOENT',
 syscall: 'open',
 path: 'D:\\Users\\yuan\\Desktop\\non-existent file',
 domain:
 Domain {
 domain: null,
 _events: { error: [Function] },
 _eventsCount: 1,
 _maxListeners: undefined,
 members: [],
 name: 'dl' },
 domainThrown: true }

3. 隐式绑定与显示绑定

当使用Domain对象的run方法指定所有监听的函数时,函数中使用的实例对象都隐式地绑定到Domain对象上。有时需要某些对象不绑定Domain上,或者绑定到另一个Domain上,因此需要显性绑定,利用add方法

domain.add(emitter)

可通过下面例子说明,我们创建一个Http服务器,并指定服务器接收到的客户端请求时,首先创建一个Domain对象,然后使用该对象的add方法分别将用于读取客户端请求数据的http.IncomingMessage对象,与用于发送服务器端响应数据的http.ServerRespanse对象绑定到Domain对象上,并制定当Domain对象那个捕获到错误时客户端返回错误信息。

  1 var http = require('http')
  2 var domain = require('domain')
  3 http.createServer(function(req,res){
  4         var d = domain.create();
  5         d.add(req)
  6         d.add(res)
  7         d.on('error',function(err){
  8                 
  9                         res.writeHead(200)
 10                         res.write('服务器接收客户端请求时发生以下错误: ')
 11                         res.end(err.message)
 12                 
 13         })
 14 
 15         res.writeHead(200)
 16         req.on('data',function(){
 17                 bibeexists();
 18                 res.write('你好')
 19                 res.end()
 20         })
 21 
 22 }).listen(8000)

接下来创建一个用于向Http服务器提供请求的模块文件代码

  1 var http = require('http')
  2 var options = {
  3                 hostname:'localhost',
  4                 port:8888,
  5                 path:'/',
  6                 method:'POST'
  7                 }
  8 var req = http.request(options,function(res){
  9                 res.setEncoding('utf8')
 10                 res.on('data',function(chunk){
 11                         console.log('响应内容:' + chunk)
 12                 })
 13         })
 14 req.write('你好')
 15 req.end('再见')

分别在不同界面运行以上两个程序,可得,服务器不停止工作,http请求端返回错误数据:

[root@kuber2 webproject]# node client.js 
响应内容:服务器接收客户端请求时发生以下错误: 
响应内容:noneexists is not defined
[root@kuber2 webproject]# 

使用了add方法绑定了Domain,可以使用remove方法解绑,即

domain.remove(emitter)

4 绑定回调函数与拦截回调函数

可以利用bind方法将一个回调函数与Domain绑定在一起
domain.bind(callback)
回调函数中如果有异常将异常抛出给domain,但本方法必须有throw err
eg:

  1 var fs = require('fs')
  2 var domain = require('domain')
  3 var d = domain.create()
  4 fs.readFile('./test.txt',d.bind(function(err,data){
  5                 if(err) {
  6 
  7                         console.log('blind绑定的回调函数抛出的异常:' + err.message)
  8                         throw err
  9                 }
 10                 else console.log(data)
 11         })
 12 )
 13 d.on('error',function(err){
 14         console.log('读取文件时发生以下错误:')
 15         console.log(err)
 16 })

运行所得结果如下:

blind绑定的回调函数抛出的异常:ENOENT: no such file or directory, open './test.txt'
读取文件时发生以下错误:
{ Error: ENOENT: no such file or directory, open './test.txt'
    at Error (native)
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: './test.txt',
  domain: 
   Domain {
     domain: null,
     _events: { error: [Function] },
     _eventsCount: 1,
     _maxListeners: undefined,
     members: [] },
  domainThrown: true }
[root@kuber2 webproject]# 

使用intercept可以拦截回调函数的异常,使用方法:

domain.intercept(callback)

eg:

  1 var fs = require('fs')
  2 var domain = require('domain')
  3 var d = domain.create()
  4 fs.readFile('./test.txt',d.intercept(function(err,data){
  5                  console.log(data)
  6         })
  7 )
  8 d.on('error',function(err){
  9         console.log('读取文件时发生以下错误:')
 10         console.log(err)
 11 })

运行结果:

[root@kuber2 webproject]# node app1.js
读取文件时发生以下错误:
{ Error: ENOENT: no such file or directory, open './test.txt'
    at Error (native)
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: './test.txt',
  domain: 
   Domain {
     domain: null,
     _events: { error: [Function] },
     _eventsCount: 1,
     _maxListeners: undefined,
     members: [] },
  domainThrown: false,
  domainBound: [Function] }
[root@kuber2 webproject]# 

5 domain堆栈的弹出与推入

当使用run,bind,intercept方法监听函数时,将把Domain对象推入domai堆栈中,可利用_stack属性查看Domain堆栈内容,堆栈中只能存放一个对象,后这会将前者挤出堆栈。
利用下列代码进行说明:

  1 var domain = require('domain')
  2 var d1 = domain.create();
  3 d1.name = "d1"
  4 var d2 = domain.create();
  5 d2.name = 'd2'
  6 console.log('原始堆栈:')
  7 console.log(domain._stack)
  8 d1.run(function(){
  9         console.log('d1对象:')
 10         console.log(d1)
 11         console.log('运行d1对象后的堆栈内容:')
 12         console.log(domain._stack)
 13 })
 14 d2.run(function(){
 15         console.log('d2对象:')
 16         console.log(d2)
 17         console.log('运行d2对象后的堆栈内容:')
 18         console.log(domain._stack)
 19 })

执行结果如下:

[root@kuber2 webproject]# node app2.js
原始堆栈:
[]
d1对象:
Domain {
  domain: null,
  _events: {},
  _eventsCount: 0,
  _maxListeners: undefined,
  members: [],
  name: 'd1' }
运行d1对象后的堆栈内容:
[ Domain {
    domain: null,
    _events: {},
    _eventsCount: 0,
    _maxListeners: undefined,
    members: [],
    name: 'd1' } ]
d2对象:
Domain {
  domain: null,
  _events: {},
  _eventsCount: 0,
  _maxListeners: undefined,
  members: [],
  name: 'd2' }
运行d2对象后的堆栈内容:
[ Domain {
    domain: null,
    _events: {},
    _eventsCount: 0,
    _maxListeners: undefined,
    members: [],
    name: 'd2' } ]
[root@kuber2 webproject]# 

可以利用exit方法将Domain对象从domain堆栈中弹出,弹出后不能再捕获错误
eg:

  1 var domain = require('domain')
  2 var d = domain.create()
  3 d.on('error',function(err){
  4         console.log('Domain对象捕获到错误')
  5 })
  6 console.log('原始堆栈:')
  7 console.log(domain._stack)
  8 d.run(function(){
  9         console.log('运行domain对象后的堆栈内容:')
 10         console.log(domain._stack)
 11         throw new Error("error")
 12 })

此时输出结果为:

[root@kuber2 webproject]# node app3.js
原始堆栈:
[]
运行domain对象后的堆栈内容:
[ Domain {
    domain: null,
    _events: { error: [Function] },
    _eventsCount: 1,
    _maxListeners: undefined,
    members: [] } ]
Domain对象捕获到错误
[root@kuber2 webproject]# 

将domain对象从堆栈中弹出:

  1 var domain = require('domain')
  2 var d = domain.create()
  3 d.on('error',function(err){
  4         console.log('Domain对象捕获到错误')
  5 })
  6 console.log('原始堆栈:')
  7 console.log(domain._stack)
  8 d.run(function(){
  9         d.exit();
 10         console.log('运行domain对象后的堆栈内容:')
 11         console.log(domain._stack)
 12 //      throw new Error("error")
 13 })
 14 

输出结果为:

[root@kuber2 webproject]# node app3.js
原始堆栈:
[]
运行domain对象后的堆栈内容:
[]

domain对象中抛出异常会是程序停止:

  1 var domain = require('domain')
  2 var d = domain.create()
  3 d.on('error',function(err){
  4         console.log('Domain对象捕获到错误')
  5 })
  6 console.log('原始堆栈:')
  7 console.log(domain._stack)
  8 d.run(function(){
  9         d.exit();
 10         console.log('运行domain对象后的堆栈内容:')
 11         console.log(domain._stack)
 12         throw new Error("error")
 13 })
 14 

输出结果从,程序停止:

/root/webproject/app3.js:12
	throw new Error("error")
	^
Error: error
at Domain.<anonymous> (/root/webproject/app3.js:12:8)
at Domain.run (domain.js:221:14)
at Object.<anonymous> (/root/webproject/app3.js:8:3)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:389:7)
[root@kuber2 webproject]# 

可以使用Domain对象的enter方法将一个Domain对象推入domain堆栈中并使该Domain对象变成当前使用的Domain对象

继续修改上述代码加入enter方法:

  1 var domain = require('domain')
  2 var d = domain.create()
  3 d.on('error',function(err){
  4         console.log('Domain对象捕获到错误')
  5 })
  6 console.log('原始堆栈:')
  7 console.log(domain._stack)
  8 d.run(function(){
  9         d.exit();
 10         console.log('运行domain对象后的堆栈内容:')
 11         console.log(domain._stack)
 12         d.enter()
 13         console.log('运行enter方法后的堆栈内容:')
 14         console.log(domain._stack)
 15         throw new Error("error")
 16 })
 17 

运行结果:

root@kuber2 webproject]# node app3.js
原始堆栈:
[]
运行domain对象后的堆栈内容:
[]
运行enter方法后的堆栈内容:
[ Domain {
    domain: null,
    _events: { error: [Function] },
    _eventsCount: 1,
    _maxListeners: undefined,
    members: [] } ]
Domain对象捕获到错误
[root@kuber2 webproject]# 

如果多个Domain对象存在嵌套,则会抛出最内层的异常。如果外层用exit方法弹出,则虽有的Domain对象都会被弹出

6. Domain对象的销毁

在一个Domain对象不再使用时,可对其进行销毁

d.dispose()
posted @ 2017-08-25 15:38  会吃鱼的鱼  阅读(514)  评论(0编辑  收藏  举报