Nodejs 这是什么bug
Nodejs 这是什么bug
我想通过domain把截获的错误返回给客户端(下面的实现是模拟一个异步异常),第一次请求没有问题,但是第二次以后浏览器都会一直处于pending状态,我的理解是第二次请求使用了第一次请求domain里面的对像,也就是说B域访问了A域下面的变量,这样跨域访问数据导致第二次以后的请求拿到的response对像都是A域这个上下文中的,而第一次请求后response已经无效了,也就是说A域这个response没用了,每次都调用response.writeHead都是无效的。当一个进程中有公共变量的时候就会出现这样的问题,求解大神解决这个问题!!
var http = require('http'),
events = require('events');
domain = require('domain'),
net = require('net'),
util = require('util'),
shareObject = null;
function InheritEventObject()
{
events.EventEmitter.apply(this);
}
util.inherits(InheritEventObject, events.EventEmitter);
var cnt = 0;
http.createServer(function(request, response) {
var rdomain = domain.create();
rdomain.data = ++cnt;
rdomain.on('error', function(err) {
console.log(this.data);//每次都是1
response.writeHead(500);
response.end(err.stack);
});
rdomain.run(function(){
var obj = getShareObject();
obj.emit("throwError");
});
}).listen(8080);
function getShareObject()
{
if(shareObject === null)
{
shareObject = new InheritEventObject();
shareObject.on("throwError", function(){
throw new Error("hello error");
});
}
return shareObject;
}
根据你的描述,你希望捕获并处理异步错误,并将这些错误返回给客户端。但你遇到了一个在多次请求中响应对象失效的问题。这主要是因为你共享了一个全局的对象 shareObject
,导致不同请求之间的数据相互影响。
分析问题
在你的代码中,shareObject
是一个全局对象,每个请求都会共享这个对象。在 getShareObject
函数中,如果 shareObject
尚未初始化,则会创建一个新的实例,并绑定一个事件处理器来抛出错误。由于所有请求都共享同一个 shareObject
实例,这意味着所有请求都会触发相同的错误处理逻辑。
解决方案
为了解决这个问题,你需要确保每个请求都有自己独立的对象实例。你可以通过在请求处理函数内部创建新的 InheritEventObject
实例来实现这一点。
修改后的代码
var http = require('http'),
events = require('events'),
domain = require('domain');
function InheritEventObject() {
events.EventEmitter.apply(this);
}
util.inherits(InheritEventObject, events.EventEmitter);
http.createServer(function(request, response) {
var rdomain = domain.create();
rdomain.on('error', function(err) {
console.error(err.stack);
response.writeHead(500);
response.end(err.stack);
});
rdomain.run(function() {
var obj = new InheritEventObject(); // 每个请求创建一个新的对象实例
obj.on("throwError", function() {
throw new Error("hello error");
});
obj.emit("throwError");
});
}).listen(8080, function() {
console.log("Server running at http://127.0.0.1:8080/");
});
解释
- 每请求独立的对象:通过在请求处理函数内部创建
InheritEventObject
的新实例,我们确保每个请求都有自己的独立对象,不会互相干扰。 - 错误处理:在
rdomain
的错误处理函数中,我们捕获并记录错误,然后向客户端返回错误信息。 - 避免全局共享:删除了全局的
shareObject
变量,防止多个请求间的数据污染。
这样修改后,每个请求将拥有独立的事件对象实例,从而避免了跨请求的数据冲突问题。
改为setHeader吧,write只能写一次
在rdomain的error里面,每次拿到的response都是第一次的。setHeader也没用。
function InheritEventObject()
{
// 删掉下面这一行就正常了,暂时没看是神马原因
// 但有一点可以肯定的是,你这种写法跟后面的util.inherits()是重复的
// events.EventEmitter.apply(this);
}
util.inherits(InheritEventObject, events.EventEmitter);
大概的原因是:
本来你想实现继承EventEmitter,直接执行util.inherits(InheritEventObject, events.EventEmitter);
即可,但是你又在InheritEventObject()
中执行了events.EventEmitter.apply(this);
,导致把当前的Domain对象绑定到了新创建的InheritEventObject对象上(其实你这种写法本来就是不对的)。以后在每次访问请求时,用的是同一个InheritEventObject对象,那么其出错的时候,也是触发同一个Domain对象的error事件,而里面的Response对象早已经关闭了,所以只要第一次请求能返回,后续的请求都没法返回出错信息。
而若在rdomain.on('error', function () {})
处理后,执行rdomain.dispose()
来销毁当前的Domain对象,则原来的InheritEventObject对象所绑定的Domain也失效,程序能正确找到当前真正的Domain,所以能正常运行:
rdomain.on('error', function(err) {
console.log(this.data);//每次都是1
response.writeHead(500);
response.end(err.stack);
rdomain.dispose();
});
EventEmitter的源码:
function EventEmitter() {
this.domain = null;
if (exports.usingDomains) {
// if there is an active domain, then attach to it.
domain = domain || require('domain');
if (domain.active && !(this instanceof domain.Domain)) {
this.domain = domain.active;
}
}
this._events = this._events || null;
this._maxListeners = this._maxListeners || defaultMaxListeners;
}
从你提供的代码来看,问题出在shareObject
对象的共享上。你在getShareObject
函数中创建了一个全局的事件对象,并且这个对象在整个应用程序运行期间只有一个实例。这意味着所有的请求都会共享同一个事件对象,从而导致域(domain)机制失效。
具体来说,你希望每个请求都有自己独立的域来处理错误,但因为所有的请求都使用了同一个shareObject
,所以当其中一个请求抛出错误时,其他请求也会受到该错误的影响。
解决方案
为了让每个请求都有自己的域,应该避免使用全局共享的对象。你可以将shareObject
的创建逻辑放在域内部,确保每个请求都有独立的对象。
下面是修改后的代码:
var http = require('http');
var domain = require('domain');
http.createServer(function(request, response) {
var rdomain = domain.create();
rdomain.on('error', function(err) {
console.error(err);
response.writeHead(500);
response.end(err.stack);
});
rdomain.run(function() {
// 在这里创建独立的事件对象
var shareObject = new events.EventEmitter();
shareObject.on("throwError", function() {
throw new Error("hello error");
});
shareObject.emit("throwError");
});
}).listen(8080, function() {
console.log('Server is running on port 8080');
});
解释
- 每个请求都有自己的域(
rdomain
)。 - 在域内部创建一个新的事件对象(
shareObject
),确保每个请求使用独立的事件对象。 - 使用
domain.create()
创建一个新域,并将其绑定到当前请求。 - 如果发生错误,错误会在域内部被捕获并处理,不会影响其他请求。
这样可以确保每个请求都能正确地捕获和处理错误,而不会相互干扰。