【发布时间】:2016-06-16 23:17:55
【问题描述】:
根据对this question 的回复,我试图弄清楚为什么让多个工作人员在同一个端口/地址上调用server.listen() 不会导致任何问题,但随后有一个旧工作人员调用server.close()在同一端口上通过server.listen() 将反复给出错误EADDRINUSE。
这似乎不是监听器没有正确关闭的情况,因为发出了close 事件,这是我尝试设置新监听器的时候。当这个工人得到EADDRINUSE时,新生成的工人可以毫无问题地调用server.listen()。
这里有一个简单的测试来演示这个问题。由于worker每100ms分叉一次,他们将在16000端口建立一个监听器。当worker 10分叉时,它将在1s后建立一个超时来关闭它的监听器。一旦发出close 事件,它将尝试再次在端口16000 上调用server.listen() 并得到EADDRINUSE 错误。为保持一致性,此测试在绑定期间显式提供相同的地址,以避免处理null 地址的核心模块出现任何潜在问题。
这个特定的实现将导致 worker 10 在绑定期间遇到错误后占用所有周期,从而阻止主进程派生新的 worker。如果在调用server.listen() 之前添加延迟,则worker 10 仍将继续点击EADDRINUSE,而master 会不断分叉能够建立侦听器的新worker。
var cluster = require('cluster');
var net = require('net');
if (cluster.isMaster) {
setInterval(function(){cluster.fork()},100);
} else {
var workerID = cluster.worker.id;
var server;
var setup = function() {
console.log('Worker ' + workerID + ' setting up listener');
server = net.createServer(function(stream) {});
server.on('error', function(err) {
console.log('Error on worker ' + workerID, err);
teardown();
});
if (workerID == 10) {
server.listen(16000, '127.0.0.1', function() {
console.log('Worker ' + workerID + ' listener established');
setTimeout(teardown, 1000);
});
} else {
server.listen(16000, '127.0.0.1', function() {
console.log('Worker ' + workerID + ' listener established');
});
}
}
var teardown = function() {
console.log('Worker ' + workerID + ' closing listener');
server.close(setup);
}
setup();
}
此测试用例的初始输出:
Worker 1 setting up listener
Worker 1 listener established
Worker 2 setting up listener
Worker 2 listener established
Worker 3 setting up listener
Worker 3 listener established
Worker 4 setting up listener
Worker 4 listener established
Worker 5 setting up listener
Worker 5 listener established
Worker 6 setting up listener
Worker 6 listener established
Worker 7 setting up listener
Worker 7 listener established
Worker 8 setting up listener
Worker 8 listener established
Worker 9 setting up listener
Worker 9 listener established
Worker 10 setting up listener
Worker 10 listener established
Worker 11 setting up listener
Worker 11 listener established
Worker 12 setting up listener
Worker 12 listener established
Worker 13 setting up listener
Worker 13 listener established
Worker 14 setting up listener
Worker 14 listener established
Worker 15 setting up listener
Worker 15 listener established
Worker 16 setting up listener
Worker 16 listener established
Worker 17 setting up listener
Worker 17 listener established
Worker 18 setting up listener
Worker 18 listener established
Worker 19 setting up listener
Worker 19 listener established
Worker 10 closing listener
Worker 10 setting up listener
Error on worker 10 { [Error: bind EADDRINUSE 127.0.0.1:16000]
code: 'EADDRINUSE',
errno: 'EADDRINUSE',
syscall: 'bind',
address: '127.0.0.1',
port: 16000 }
Worker 10 closing listener
Worker 10 setting up listener
Error on worker 10 { [Error: bind EADDRINUSE 127.0.0.1:16000]
code: 'EADDRINUSE',
errno: 'EADDRINUSE',
syscall: 'bind',
address: '127.0.0.1',
port: 16000 }
Worker 10 closing listener
【问题讨论】:
-
如果您侦听端口 0 而不是 16000(这意味着侦听端口将是随机的,但所有工作人员将使用相同的随机端口),则不会引发错误,所以我想知道如果 this code 可能导致错误(尽管它没有多大意义,因为 AFAICS 这意味着端口不匹配,这是不应该的)。
-
通过使用端口 0 而不是 16000,所有初始侦听器都建立在同一个端口上(在我的例子中是 53230),但是每当 worker 10 使用端口 0 启动另一个侦听器时,似乎增加正在侦听的端口(53230 用于初始侦听,53231 用于第二次侦听,等等)。
-
呃,我没有意识到这一点。听起来像第二次在工作人员中调用
server.listen()充满了问题。 -
你能打印出套接字或服务器对象本身吗?这可能会为您提供有关它为何再次尝试调用
bind的更多详细信息。我猜在其他工作人员的情况下,他们知道套接字存在,因此他们避免调用bind,但对于工作人员10 它错误地认为套接字没有被绑定。问题是找出在这个有状态的操作中哪里出错了。