Node.js 集群模块未在 http.Server.close() 上发布 socket/port
Node.js cluster module doesn't release socket/port on http.Server.close()
在 Node.js 脚本中,我试图打开一些 ports/sockets 然后再次关闭它们。我在使用 Node.js 集群模块时遇到了麻烦 运行。当不使用 Cluster 模块时,它似乎没有以与相同代码相同的方式关闭端口。
例如,采用以下代码,它在自己的端口上创建 5 个 http.Server,然后关闭除最后一个(第 5 个)之外的所有端口。
'use strict';
var http = require('http');
function goGoGadget(i) {
console.log('var i = ', i);
var httpServer = http.createServer();
var nextPort = 4000 + i;
console.log('about to listen on port', nextPort);
httpServer.listen(nextPort);
if (i < 5) {
httpServer.on('listening', function() {
console.log('closing port:', nextPort);
httpServer.close(function() {
console.log('closed server on port:', nextPort);
});
});
}
}
goGoGadget(1);
goGoGadget(2);
goGoGadget(3);
goGoGadget(4);
goGoGadget(5);
不出所料,node.js:
只打开了一个端口
dhcp50:test marco$ lsof -i -n -P | grep node
node 20895 marco 16u IPv4 0xb242422d2e0e2bb1 0t0 TCP *:4005 (LISTEN)
然后,相同的代码,但 运行 在使用集群模块的工作人员中:
'use strict';
var cluster = require('cluster');
var http = require('http');
function goGoGadget(i) {
console.log('var i = ', i);
var httpServer = http.createServer();
var nextPort = 4000 + i;
console.log('about to listen on port', nextPort);
httpServer.listen(nextPort);
if (i < 5) {
httpServer.on('listening', function() {
console.log('closing port:', nextPort);
httpServer.close(function() {
console.log('closed server on port:', nextPort);
});
});
}
}
if (cluster.isMaster) {
var numWorkers = 5;
for (var i = 0; i < numWorkers; i += 1) {
console.log ('forking ', i + 1);
cluster.fork();
}
}
else {
goGoGadget(cluster.worker.id);
}
这一次,Node.js 继续保留前 4 个 http.Server 的端口,即使它们已经关闭:
dhcp50:test marco$ lsof -i -n -P | grep node
node 20900 marco 17u IPv4 0xb242422d2d9682e1 0t0 TCP *:4004 (LISTEN)
node 20900 marco 18u IPv4 0xb242422d2d5faa11 0t0 TCP *:4001 (LISTEN)
node 20900 marco 19u IPv4 0xb242422d2d5fd621 0t0 TCP *:4005 (LISTEN)
node 20900 marco 20u IPv4 0xb242422d2d985141 0t0 TCP *:4002 (LISTEN)
node 20900 marco 21u IPv4 0xb242422d2d9862e1 0t0 TCP *:4003 (LISTEN)
node 20905 marco 13u IPv4 0xb242422d2d5fd621 0t0 TCP *:4005 (LISTEN)
我的问题很简单:为什么?以及如何让他们关闭?我怀疑如果我在 net.Server.listen() the ports might close, but the http.Server.listen() 中使用 exclusive 选项 API 似乎不会将此选项散列作为参数。
这个问题的起源是在使用集群模块的 Express.js 应用程序中使用 phantom-node(与 phantomjs)随着时间的推移打开许多页面时出现的问题。为了确保在创建新的幻影节点实例时没有端口冲突,并避免 EADDRINUSE
,我进行了端口扫描并检查我将要使用的端口是否可用。问题是,随着时间的推移,越来越多的端口被打开,并且由于它们从未关闭,节点最终崩溃并出现 EMFILE
错误。
我注意到的可能是您的部分答案:有时,系统在要求关闭时不会释放端口。我遇到了一个小 shell 脚本永远关闭(nodejs 持久性模块)的问题,从 git 中提取新代码并重新启动它。服务器不会再次启动,因为它说 "port xxxx already in use"。
看来,所描述的问题出现在 Node v0.11.1 之前。从 0.11.2 开始,套接字在关闭时成功取消链接。由 this commit.
修复
酷
在 Node.js 脚本中,我试图打开一些 ports/sockets 然后再次关闭它们。我在使用 Node.js 集群模块时遇到了麻烦 运行。当不使用 Cluster 模块时,它似乎没有以与相同代码相同的方式关闭端口。
例如,采用以下代码,它在自己的端口上创建 5 个 http.Server,然后关闭除最后一个(第 5 个)之外的所有端口。
'use strict';
var http = require('http');
function goGoGadget(i) {
console.log('var i = ', i);
var httpServer = http.createServer();
var nextPort = 4000 + i;
console.log('about to listen on port', nextPort);
httpServer.listen(nextPort);
if (i < 5) {
httpServer.on('listening', function() {
console.log('closing port:', nextPort);
httpServer.close(function() {
console.log('closed server on port:', nextPort);
});
});
}
}
goGoGadget(1);
goGoGadget(2);
goGoGadget(3);
goGoGadget(4);
goGoGadget(5);
不出所料,node.js:
只打开了一个端口dhcp50:test marco$ lsof -i -n -P | grep node
node 20895 marco 16u IPv4 0xb242422d2e0e2bb1 0t0 TCP *:4005 (LISTEN)
然后,相同的代码,但 运行 在使用集群模块的工作人员中:
'use strict';
var cluster = require('cluster');
var http = require('http');
function goGoGadget(i) {
console.log('var i = ', i);
var httpServer = http.createServer();
var nextPort = 4000 + i;
console.log('about to listen on port', nextPort);
httpServer.listen(nextPort);
if (i < 5) {
httpServer.on('listening', function() {
console.log('closing port:', nextPort);
httpServer.close(function() {
console.log('closed server on port:', nextPort);
});
});
}
}
if (cluster.isMaster) {
var numWorkers = 5;
for (var i = 0; i < numWorkers; i += 1) {
console.log ('forking ', i + 1);
cluster.fork();
}
}
else {
goGoGadget(cluster.worker.id);
}
这一次,Node.js 继续保留前 4 个 http.Server 的端口,即使它们已经关闭:
dhcp50:test marco$ lsof -i -n -P | grep node
node 20900 marco 17u IPv4 0xb242422d2d9682e1 0t0 TCP *:4004 (LISTEN)
node 20900 marco 18u IPv4 0xb242422d2d5faa11 0t0 TCP *:4001 (LISTEN)
node 20900 marco 19u IPv4 0xb242422d2d5fd621 0t0 TCP *:4005 (LISTEN)
node 20900 marco 20u IPv4 0xb242422d2d985141 0t0 TCP *:4002 (LISTEN)
node 20900 marco 21u IPv4 0xb242422d2d9862e1 0t0 TCP *:4003 (LISTEN)
node 20905 marco 13u IPv4 0xb242422d2d5fd621 0t0 TCP *:4005 (LISTEN)
我的问题很简单:为什么?以及如何让他们关闭?我怀疑如果我在 net.Server.listen() the ports might close, but the http.Server.listen() 中使用 exclusive 选项 API 似乎不会将此选项散列作为参数。
这个问题的起源是在使用集群模块的 Express.js 应用程序中使用 phantom-node(与 phantomjs)随着时间的推移打开许多页面时出现的问题。为了确保在创建新的幻影节点实例时没有端口冲突,并避免 EADDRINUSE
,我进行了端口扫描并检查我将要使用的端口是否可用。问题是,随着时间的推移,越来越多的端口被打开,并且由于它们从未关闭,节点最终崩溃并出现 EMFILE
错误。
我注意到的可能是您的部分答案:有时,系统在要求关闭时不会释放端口。我遇到了一个小 shell 脚本永远关闭(nodejs 持久性模块)的问题,从 git 中提取新代码并重新启动它。服务器不会再次启动,因为它说 "port xxxx already in use"。
看来,所描述的问题出现在 Node v0.11.1 之前。从 0.11.2 开始,套接字在关闭时成功取消链接。由 this commit.
修复酷