可以从处理这些操作的控制面板启动和停止每个应用程序.
启动应用程序时,控制面板会执行以下操作:
>从以root身份运行的端口绑定器请求句柄.此端口绑定器是控制面板的分叉子节点.
>现在,控制面板将侦听此端口上的请求.收到请求后,它会将客户端套接字的句柄发送给正在侦听“message”事件的应用程序以获取新请求.
>一旦发出应用程序信号,控制面板就会释放对请求和套接字的所有控制权.
>应用程序执行它需要做的事情,并释放和关闭请求和套接字.
此设置很有效,除非尝试关闭不再需要的服务器.
即使服务器发出’close’事件,它仍然接受连接,最终超时,因为不再有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',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 ); });
正如您所看到的,我正在尝试null并分离我拥有的所有_handle属性,但服务器句柄不会停止侦听端口.
当我使控制面板有一个未捕获的异常时,它会崩溃,端口绑定器也会这样做.但是应用程序节点进程没有退出并且端口保持绑定,所以我发现我没有正确地释放那些东西,但我无法找到它.
整个设置确实有效,当我提出要求时,它根本就没有关闭服务器.
编辑:我应该注意到我使用的是node.js v0.5.10
解决方法
我使用的浏览器发送了一个keep-alive头,保持连接打开一段时间,以通过相同的连接推送新的请求.
当我做了server.close()时,我希望关闭这些连接,因为文档说:
Stops the server from accepting new connections.
事实证明,保持活动连接没有关闭,所以只要连接打开,浏览器仍然可以做请求,让我知道服务器仍在接受连接.
解决方法是在关闭后将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;