node 事件监听超出10个以上怎么处理?
发布于 9 年前 作者 xchl 5786 次浏览 最后一次编辑是 8 年前 来自 问答

代码抄自《深入浅出nodejs》

	var EventEmitter = require('events').EventEmitter;
	var event = new EventEmitter();
	var fs = require('fs');
	var status = 'ready';
	var readFile = function(callback){
		event.once('read',callback)
		if(status == 'ready'){
			status = 'reading';
			fs.readFile('a.json','utf-8',function(){
				event.emit('read',res);
				status = 'ready';
			})
		}
	}
	//下面会出警告监听器超出10有内存溢出的危险。
	for (var i = 0; i < 11; i++){   
		readFile(function(res){
			console.log(res)
		});
	}

监听器超过10个node就会出警告有内存溢出的危险,想知道这种情况除了setMaxListenters()还有其他解决方案吗。或者其他思路实现上例中的方法。

16 回复

按照楼主给出了代码测试了下,的确报错,但其实错误是说明event监听read事件方法的个数超出了最大数量。 按照你给出的代码是想顺序读取同一个文件的吧,但这在node是不可能的。

@joney-pinkman 这里想实现的效果这样的,for循环那里是模仿同时11次请求。比如同时11条请求都是读取同一个文件,给11条请求都加上监听。然后只读一次文件,而不用读11次。

放心吧,一般情况下不会超过10个的。你可以假设下即使是做消息转发等业务,也仅仅是定义一个消息协议,监听一个,至于不同的消息类型是内部控制的,再加上node本身不适合做大型的server端,所以不必担心。当然,或许你的场景有这样的需求,不妨一起讨论下。

@xchl 你的这个思路有点不大对,对于读取文件或者请求数据等操作,一般情况下都是很快的,你可以理解为请求完后能够立即拿到回执,所以并行的的11次和串行的11次没多大关系,其次对于频繁访问的数据一般操作都是建立缓存,可以使用redis、memcache等,利用其高效的访问速度进行处理。

@haozxuan 恩 我也觉的有点纠结,这个思路在请求并发的时候不会对数据库,或者是io上造成太大的压力,但会有内存泄露的危险。下面是这个思路完整的代码。觉的作者这个思路很好,就是不知道怎么规避事件监听的问题

./ab -n11 -c11 http://127.0.0.1:1337/

var http = require('http');
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
var fs = require('fs');
var status = 'ready';
var readFile = function(callback){
	event.once('read',callback);
	if(status === 'ready'){
    	console.log('这里只会读取一次文件');
    	status = 'pending';
    	fs.readFile('a.json','utf-8',function(err,res){
        	event.emit('read',res);
        	status = 'ready';
    	});
	}
}
http.createServer(function(req,res){
	res.writeHead(200, {'Content-Type': 'text/plain'});
	readFile(function(res){
    	console.log(res);
	})
	res.end('res');
}).listen(1337,'127.0.0.1');

@xchl 你的需求并不明确,贴出来的这部分代码的确是一个经典的demo,但并不能作为一个可用的server端,因为每次http请求的目的是获取a.json的内容,那么完全不必要注册事件,可以直接去读取文件。

@haozxuan 这里绑定事件的想法是这样的, 同样的并发数下测试 ./ab -n11 -c11 http://127.0.0.1:1337,如果直接读取的话会读11次这个文件,按照上面的思路只会读1次,相比直接读少了10次。

cluster 模式下怎么保证呢?

@xchl 的确是这样的,不过有些情况并不适合这种模式,比如php的接口调用。当他需要立即返回数据而你做的不仅仅是读数据这么简单的一步操作就会造成异常。 还有一种情况,如果第一次请求数据的时间点文件内容为a、b、c,第10次请求数据的时间点文件内容为a、b、c、d这样就会造成脏数据。建议使用redis等缓存将数据存取下来。

@iamcc 估计cluster模式下就要共享status态了。。。

@iamcc cluster还没太接触,最近刚开始用node做项目。 @haozxuan 最后加Redis了 多谢指点

之所以循环11执行1次readFile是因为你的判断,第一次条件满足成立时将status 改为 pending了。而在此改为ready则是在文件读取回调中。 然而循环的速度非常快,并且是在当前逻辑下,所以他必须先执行完毕,那么久导致你once了十一次,实际上emit得只有一次。 另外,我once是监听一次的,如果你多次重复once一个相同的event,怎么都觉得你对不起设计once的那个人吧。

其实楼主可以换个思路吧,用缓存的方式,先将第一次请求文件缓存,其余请求等待,缓存完毕后其余请求全部读取缓存,最后一次请求删除缓存,这样不知是否满足楼主的需求呢

@forfuns 那里的循环模仿的是11条并发请求的情况,每条请求都绑定一个once(这里用once是保证每一条请求只触发一次)

@joney-pinkman 既然第一条请求已经拿回结果了可以直接给后面的请求,再放缓存里意义不是很大吧。这段代码主要是并发的时候能看出作用,同时弊端就是监听太多事件。。。

@coordcn 恩 学习了,谢谢

回到顶部