如何正确处理子进程中的句柄

时间:2011-11-05 01:21:49

标签: node.js

我正在尝试构建一个平台来自动部署我在NodeJS中构建的应用程序。 可以从处理这些操作的控制面板启动和停止每个应用程序。 启动应用程序时,控制面板会执行以下操作:

  • 从以root身份运行的port-binder请求句柄。此port-binder是控制面板的分叉子项。
  • 现在,控制面板会侦听此端口上的请求。收到请求后,它会将客户端套接字的句柄发送给正在侦听“消息”的应用程序。新请求的活动。
  • 一旦发出应用程序信号,控制面板就会释放对请求和套接字的所有控制权。
  • 应用程序执行它需要做的事情,并释放和关闭请求和套接字。

此设置很有效,除非尝试关闭不再需要的服务器。 即使服务器发出'关闭'事件,它仍然接受连接,最终超时,因为不再有requestListener绑定。

我假设有一个没有正确释放的句柄,从而使服务器保持清醒状态。

端口绑定器:

var options = {
    http: require('http'),
    https: require('https')
};
process.on('message',function( data, handle ) {

    var port = data.port,
            type = data.type,
            server;

    server = options[type].createServer();
    server.once('error', function( err ) {
        process.send({success: false, port: port, error: err});
    });
    server.listen(port,function(){
        var handle = server._handle;

        process.send({success: true, port: port}, handle);

        server.close();
        handle.close();
        server._handle = null;
        handle = null;
        server.removeAllListeners();
    });
});

控制面板:

var cp = require('child_process'),
    app,
    types = {
        http: require('http'),
        https: require('https')
    },
    binder = cp.fork('./portbinder');

binder.on('message', function( data, handle ) {
    var server = types['http'].createServer(handler);

    server.once('close', function() {
        server._handle = null;
        handle = null;
    });
    server.listen( handle );
});

function handler( req, resp ) {
    app = app || cp.fork('./application'),
    socket = resp.socket,

    _handle = socket._handle,
    _req = {},
    _resp = {},
    _socket = {};

    // Copy transferable fields.
    for( i in req ) {
        if( typeof req[i] != 'function' && i != 'socket' && i != 'connection' && i != 'client' ) {
            _req[i] = req[i];
        }
    }

    for( i in resp ) {
        if( typeof resp[i] != 'function' && i != 'socket' && i != 'connection' && i != 'client' ) {
            _resp[i] = resp[i];
        }
    }

    for( i in socket ) {
        if( typeof socket[i] != 'function' &&
            i != '_handle' && i != '_httpMessage' &&
            i != '_idlePrev' && i != '_idleNext' && i != 'server' ) {
                _socket[i] = socket[i];
        }
    }

    // Send request to application.
    app.send({
        type: 'request',
        data: {
            req: _req,
            resp: _resp,
            socket: _socket
        }
    }, _handle );

    // Release the handle here without sending anything to the browser.
    socket._write = function() {};
    resp.end();
    socket.destroy();
    socket._handle = null;
    _handle = null;
};

应用:

function reattach( target, properties ) {
    var i;

    for( i in properties ) {
        target[i] = properties[i];
    }

    return target;
};

function handler( req, resp ) {
    resp.end('hello world!');
};

process.on('message', function( param, handle ) {
    var _req = param.data.req,
        _resp = param.data.resp,
        _socket = param.data.socket,

    socket, resp, req;

    // Create a socket and reattach its properties.
    socket = new net.Socket({handle:handle});
    reattach( socket, _socket );

    // Create a request and reattach its properties.
    req = new http.IncomingMessage(socket);
    reattach( req, _req );

    // Create a response and reattach its properties.
    resp = new http.ServerResponse( req );
    resp.assignSocket(socket);
    reattach( resp, _resp );

    // Help closing down sockets after request is handled.
    resp.once('finish', function() {
        socket._write = function() {};
        socket.end();
        socket.destroy();
        socket._handle = null;
        handle = null;
    });

    handler( req, resp );
});

正如您所看到的,我尝试将所有_handle属性置空并分离,但服务器句柄不会停止侦听端口。 当我使控制面板有一个未捕获的异常时,它会崩溃,端口绑定器也会这样做。但是应用程序节点进程没有退出并且端口保持绑定,所以我想说我没有正确地释放那些东西,但是我找不到它。

整个设置确实有效,当我提出要求时,它根本就没有关闭服务器。

编辑:我应该注意到我使用的是node.js v0.5.10

1 个答案:

答案 0 :(得分:8)

事实证明,这是对node.js文档的错误解释导致了这个问题。 我使用的浏览器发送了一个keep-alive头,保持连接打开一段时间,以便通过相同的连接推送新的请求。

当我做了server.close()时,我希望关闭这些连接,因为文档说:

  

停止服务器接受新连接。

事实证明,保持活动连接没有关闭,所以只要连接打开,浏览器仍然能够发出请求,让我知道服务器仍在接受连接。

解决方法是在关闭后将requestListener重新连接到服务器。然后,当您收到连接时,关闭套接字流而不发送任何内容。现在不会处理任何新请求,一旦连接超时,它将关闭,服务器将最终完全关闭。从而为其他用途释放了端口。

server.close();
// Drop any new request immediately, while server ditches old connections.
server.on('request', function( req, resp ) { req.socket.end(); });
server.once('close', function(){
    // Remove the listeners after the server has shutdown for real.
    server.removeAllListeners();
});

将此修复程序放到位我还可以清理代码并删除代码以释放句柄。 node.js垃圾收集器现在正确地获取句柄对象,当连接死亡时,句柄也就这样。

// All of this is no longer needed.
socket._write = function() {};
socket.end();
socket.destroy();
socket._handle = null;
handle = null;