Node.js:这个长轮询实现是不是泄漏:

Posted

技术标签:

【中文标题】Node.js:这个长轮询实现是不是泄漏:【英文标题】:Node.js: Does this long polling implementation leak:Node.js:这个长轮询实现是否泄漏: 【发布时间】:2012-08-24 23:24:25 【问题描述】:

我正在评估 Node.js 是否可以替代我当前在 Java Web 应用程序上的推送功能。我编写了一个简单的长轮询服务器,它就像客户端和 Java 后端之间的中介一样工作。客户端发出订阅请求,然后 Java 服务器可以通过调用 Node.js 通知订阅的客户端。到目前为止,它似乎工作正常,但我收到以下消息,指出内存泄漏:

    (node) warning: possible EventEmitter memory leak detected. 11 listeners added.
Use emitter.setMaxListeners() to increase limit.
Trace
    at EventEmitter.addListener (events.js:168:15)
    at EventEmitter.once (events.js:189:8)
    at route (C:\Users\Juan Pablo\pushserver.js:42:12)
    at Server.onRequest (C:\Users\Juan Pablo\pushserver.js:32:3)
    at Server.EventEmitter.emit (events.js:91:17)
    at HTTPParser.parser.onIncoming (http.js:1793:12)
    at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:111:23
)
    at Socket.socket.ondata (http.js:1690:22)
    at TCP.onread (net.js:402:27)

我有一行代码在发出通知事件时记录现有的侦听器。我已经让它运行了一段时间,它显示每个订阅的客户端只有一个侦听器(应该是这样),但是当我收到警告消息时,这条线不在代码上。代码完全一样,除了那条线很硬。

这是推送服务器的代码(因为我还在学习 Node.js,所以有点初级):

var http = require('http');
var url = require("url");
var qs = require("querystring");
var events = require('events');
var util = require('util');

var emitter = new events.EventEmitter;

function onRequest(request, response)

    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");
    request.setEncoding("utf8");

    if (request.method == 'POST')
    
        var postData = "";
        request.addListener("data", function(postDataChunk)
        
            postData += postDataChunk;
            console.log("Received POST data chunk '"+ postDataChunk + "'.");
        );

        request.addListener("end", function()
        
            route(pathname, response, postData);
        );
    
    else if (request.method=='GET')
    
        var urlParts = url.parse(request.url, true);
        route(pathname, response, urlParts.query);
    


function route(pathname, response, data)

    switch (pathname)
    
        case "/subscription":

            emitter.once("event:notify", function(ids)
            
                response.writeHead(200, "Content-Type": "text/html", "Access-Control-Allow-Origin": "*");
                response.write(JSON.stringify(ids));
                response.end();
            );

            break;

        case "/notification":

            //show how many listeners exist
            console.log(util.inspect(emitter.listeners('event:notify'));

            emitter.emit("event:notify", data.ids);

            response.writeHead(200, "Content-Type": "text/html", "Access-Control-Allow-Origin": "*");
            response.write(JSON.stringify(true));
            response.end();
            break;

        default:

            console.log("No request handler found for " + pathname);
            response.writeHead(404, "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*");
            response.write("404 - Not found");
            response.end();
            break;
    


http.createServer(onRequest).listen(8888);

console.log('Server running at http://127.0.0.1:8888/');

我的印象是使用emitter.once 会在使用后自动删除事件监听器,所以我不知道如果只连接一个客户端,如何添加11 个监听器。我在想,如果客户端在等待通知时断开连接,那么相关的连接资源就不会被释放。

我想知道我是否必须手动处理断开连接,以及那里是否确实存在泄漏。欢迎任何建议。谢谢。

【问题讨论】:

【参考方案1】:

如果有人感兴趣,上面的代码确实会泄漏。当客户端在发送通知之前断开连接时,就会发生泄漏。为了解决这个问题,需要在客户端突然断开连接时移除事件监听器,例如:

case "/subscription":

        var notify = function(ids)
        
            response.writeHead(200, "Content-Type": "text/html", "Access-Control-Allow-Origin": "*");
            response.write(JSON.stringify(ids));
            response.end();
        

        emitter.once("event:notify", notify);

        //event will be removed when connection is closed
        request.on("close", function()
        
            emitter.removeListener("event:notify", notify);
        );
        break;

【讨论】:

以上是关于Node.js:这个长轮询实现是不是泄漏:的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 socket.io 实现长轮询?

Gevent的长轮询实现方法详解

gevent中如何实现长轮询

长轮询服务器原理(客户端识别)

SQS 短轮询是不是比长轮询更可取?

[当套接字可用时使用HTTP长轮询(例如iPhone,Blackberry)