Node.js异步异常处理和域模块解析

Node.js异步异常处理和域模块解析
异步异常处理

异步异常特征

由于节点的回调异步,是不可能捕获所有异常的catch:
{试
process.nexttick(函数(){()
foo.bar();
});
} catch(错误){
无法捕捉到它
}
就Web服务而言,这是非常希望的:
快速类型路由
app.get( /索引的功能(REQ,RES){
{试
业务逻辑
} catch(错误){
Logger.error(ERR);
res.statuscode = 500;
返回res.json({成功:假消息服务器异常});
}
});
如果尝试捕获可以捕获所有异常,当代码有一些意外错误时,我们可以记录一些错误,同时返回一个500错误给调用者友好。不幸的是,尝试捕获不能捕获异步中的异常。所以我们唯一能做的就是:
app.get( /索引的功能(REQ,RES){
业务逻辑
});

Process.on ('uncaughtException', function (ERR) {
Logger.error(ERR);
});
这时,虽然我们可以记录错误的日志,进程不会退出,但是我们没有办法返回错误的请求,只能使它及时返回。



在节点V0.8 +版本的时候,一个模块领域出版了。这是什么模块不try catch不能做:捕捉异步回调的例外。

因此,上面这个无助的例子似乎有了一个解决方案:
VaR域=需要('domain);

引入域中间件,每个请求将被包装在一个单独的域中。
用于处理异常的域
app.use(功能(REQ,RES,下){
var a domain.create();
域监视错误事件
D.on(错误,函数(ERR){
Logger.error(ERR);
res.statuscode = 500;
res.json({成功:虚假,消息:服务器例外});
D.dispose();
});

D.增加(需求);
D.加(RES);
D.run(下);
});

app.get( /索引的功能(REQ,RES){
企业
});

我们以中间件的形式引入域来处理异步中的异常。当然,虽然域捕获异常,但由于异常堆栈丢失会导致内存泄漏,所以当发生这种情况或需要重启进程时,有兴趣的同学可以查看域中间件域中间件。

一个奇怪的故障

我们所有的测试都是正常的,当它们在生产环境中被正式使用时,我们发现域名突然失败了!它没有在异步捕获异常,最终导致异常退出的过程。经过调查,最终发现是通过Redis来存储会话的引入使。
var http =需要('http');
VaR连接=需要('connect);
无功redisstore =需要('connect-redis ')(连接);
无功domainmiddleware =需要('domain-middleware);

VaR服务器= http.createserver();
var =连接();
app.use(connect.session({)
关键:关键的,
秘密:秘密,
店:新redisstore(6379,'localhost)
});
/ / domainmiddleware使用可以看前面的链接
app.use(domainmiddleware({)
服务器:服务器,
KillTimeout:30000
});

此时,当我们的业务逻辑代码中有一个异常时,它被发现不被域捕获!经过几次尝试,终于找到了问题所在。
VaR域=需要('domain);
VaR redis =需要('redis);
VaR缓存= redis.createclient(6379,'localhost);

函数错误(){
cache.get('a',函数(){(){
把新的错误(错误);
});
}

函数ok(){
setTimeout(){()函数(
把新的错误(错误);
},100);
}
var a domain.create();
D.on(错误,函数(ERR){
console.log(ERR);
});

D.run(OK); / /域捕获到异常
D.run(错误); / /抛出异常
奇怪的!所有都是异步调用,为什么前者被捕获,后者不能捕获它

域分析

回首过去,让我们看看域所做的使我们能够捕获异步请求(从节点v0.10.4,可迅速改变和优化代码)。

节点事件循环机制

以前我们看域原则,让我们知道nexttick和_tickcallback两方面。
功能latercall(){
console.log('print我以后);
}

process.nexttick(latercallback);
console.log('print我第一);
上面的代码是人写的节点的熟悉,和nexttick的作用是把latercallback在接下来的事件循环执行,_tickcallback方法是一种非公开方式。它被称为入口函数,在循环结束后继续下一个事件循环。

换句话说,节点维护的事件循环nexttick团队队列,_tickcallback柱。

实现域

在了解了节点的事件循环机制之后,让我们看看域已经做了什么。

域本身实际上是通过捕获错误EventEmitter对象通过一个事件。所以当我们研究它,我们可以简化为两点:

何时触发域的错误事件:

进程抛出异常,不会被任何catch捕获。这将触发此时整个过程的processfatal。如果域包裹,它会触发错误事件域,否则,它会触发事件过程的uncaughtException。
域是如何在许多不同的事件周期中传递的:

当域名被实例化时,我们通常叫它的run方法,如在Web服务领域的例子,在包的执行功能。当包裹函数被执行时,该process.domain全局变量将指向域实例。当这一事件循环抛出,当异常被抛出processfatal研究发现,process.domain的存在,和错误事件是在域触发。
后域模块介绍中的要求,全球nexttick和_tickcallback改写,有些领域相关的代码注入。
/简化域传输代码
功能nextdomaintick(回调){
NextTickQueue.push({回调:回调,域的过程。域});
}

功能_tickdomaincallback(){
VaR滴答= nexttickqueue.pop();
/ / process.domain = tock.domain
Tock.domain tock.domain.enter();
回调();
/ /清晰的process.domain
Tock.domain tock.domain.exit();
}
};
这是一批在流通领域转移的关键事件:nexttick团队,记录当前域,当添加到队列中的周期事件是_tickcallback开始实施的时候,在process.domain记录域事件的新周期。这样,在代码包由域,不管怎么叫process.nexttick,域将通过。

当然,有一个异步节点两种情况,在事件的形式。所以在构造函数具有以下代码EventEmitter:

如果(出口。usingdomains){
如果有一个活动的域,然后附加到它。
域为域需要('domain)| |;
如果(domain.active!(这是域。域)){
this.domain = domain.active;
}
}
当实例化EventEmitter,它将绑定对象到当前域。当发出触发此对象上的一个事件,就像_tickcallback执行时,回调函数将重新由当前域包裹。

另一个案例中,setTimeout和setInterval,同样,在计时器的源代码,我们也可以找到这样的代码:

如果(过程域)timer.domain = process.domain;
像EventEmmiter,定时器的回调函数也会被包裹在当前域。

节点插入域代码中的三个关键的地方nexttick,定时器,和事件允许他们通过不同的事件循环。

更复杂的领域

在某些情况下,我们可能会遇到更复杂域使用的需求。

域嵌套:我们可以在外层中拥有域,内层还有其他域,并且可以在文档中找到使用场景。

为服务器创建一个顶级域
无功serverdomain = domain.create();

ServerDomain.run(function(){()
服务器是在serverdomain范围/创建
http.createserver(功能(REQ,RES){
要求和RES也创作了/在serverdomain范围
但是,我们希望每个请求都有一个单独的域。
首先 / /创造的东西和添加要求和研究它。
VAR必须= domain.create();
Reqd.add(需求);
Reqd.add(RES);
Reqd.on(错误,函数(ER){
Console.error(错误的,呃,请求的URL);
{试
(500)res.writehead;
res.end(错误发生了,对不起。);
} catch(ER){
Console.error(错误发送500,呃,请求的URL);
}
});
}听(1337);
});
为了实现这个功能,事实上,域也会秘密地维护一个堆栈域,感兴趣的童鞋可以在这里看到。

回去解决疑问

回过头来看,我们将看到刚才的问题:为什么这两个看起来相同的异步调用,但一个域不能捕捉异常在了解原理,不难想到,调用redis是在领域范围的异步调用,这是被错误的事件循环。让我们看一个短的代码来看看是哪里的问题。
VaR域=需要('domain);
Var EventEmitter要求EventEmitter('events);

var =新EventEmitter();

VaR定时器= setTimeout()函数(){
E.emit(数据的);
},10);

函数下一步(){
E.once(数据,函数(){(){
把新的错误(错);
});
}

var a domain.create();
D.on(错误,函数(){(){
console.log('cache由域);
});

D.run(下);
我们还发现,误差不会域捕获的原因是显而易见的:在初始化时间定时器和两个关键对象不在域的范围因此,当显示器在事件的下一个函数被触发,回调函数执行异常抛出,实际上是域包,当然不会被捕获的异常域!

事实上,节点专门设计一个API:这种情况domain.add。它可以定时和事件对象的当前域添加外部域。对于上面的例子:
添加(定时器);

d.加(e);
将任何定时器或E对象添加到域中可以让该错误被域捕获。

看那第一个redis原因域无法捕获异常的问题。我们有一个解决的办法,太

事实上,在这种情况下,仍然没有办法达到最佳的解决方案。现在,当发生意外异常时,我们只能让当前请求超时,然后让进程停止服务,然后重新启动

__domain是非常强大的,但不是万能的。__希望看完这篇文章后,您可以使用空间,避免踩到坑。
以上是本文的全部内容,希望能对您有所帮助,希望大家多多支持

免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部