cluster: respect listen backlog set by workers

PR-URL: https://github.com/nodejs/node/pull/41623
Co-authored-by: Ouyang Yadong <oyydoibh@gmail.com>
Reviewed-By: Ouyang Yadong <oyydoibh@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
Elad Nava 2022-01-20 22:24:30 -05:00 committed by Tobias Nießen
parent fe7ca085a7
commit 217366e308
4 changed files with 54 additions and 3 deletions

View File

@ -464,6 +464,10 @@ server.listen({
}); });
``` ```
When `exclusive` is `true` and the underlying handle is shared, it is
possible that several workers query a handle with different backlogs.
In this case, the first `backlog` passed to the master process will be used.
Starting an IPC server as root may cause the server path to be inaccessible for Starting an IPC server as root may cause the server path to be inaccessible for
unprivileged users. Using `readableAll` and `writableAll` will make the server unprivileged users. Using `readableAll` and `writableAll` will make the server
accessible for all users. accessible for all users.

View File

@ -15,7 +15,7 @@ const { constants } = internalBinding('tcp_wrap');
module.exports = RoundRobinHandle; module.exports = RoundRobinHandle;
function RoundRobinHandle(key, address, { port, fd, flags }) { function RoundRobinHandle(key, address, { port, fd, flags, backlog }) {
this.key = key; this.key = key;
this.all = new SafeMap(); this.all = new SafeMap();
this.free = new SafeMap(); this.free = new SafeMap();
@ -24,16 +24,17 @@ function RoundRobinHandle(key, address, { port, fd, flags }) {
this.server = net.createServer(assert.fail); this.server = net.createServer(assert.fail);
if (fd >= 0) if (fd >= 0)
this.server.listen({ fd }); this.server.listen({ fd, backlog });
else if (port >= 0) { else if (port >= 0) {
this.server.listen({ this.server.listen({
port, port,
host: address, host: address,
// Currently, net module only supports `ipv6Only` option in `flags`. // Currently, net module only supports `ipv6Only` option in `flags`.
ipv6Only: Boolean(flags & constants.UV_TCP_IPV6ONLY), ipv6Only: Boolean(flags & constants.UV_TCP_IPV6ONLY),
backlog,
}); });
} else } else
this.server.listen(address); // UNIX socket path. this.server.listen(address, backlog); // UNIX socket path.
this.server.once('listening', () => { this.server.once('listening', () => {
this.handle = this.server._handle; this.handle = this.server._handle;

View File

@ -1386,6 +1386,7 @@ function listenInCluster(server, address, port, addressType,
addressType: addressType, addressType: addressType,
fd: fd, fd: fd,
flags, flags,
backlog,
}; };
// Get the primary's server handle, and listen on it // Get the primary's server handle, and listen on it

View File

@ -0,0 +1,45 @@
'use strict';
const common = require('../common');
const assert = require('assert');
// Monkey-patch `net.Server.listen`
const net = require('net');
const cluster = require('cluster');
// Force round-robin scheduling policy
// as Windows defaults to SCHED_NONE
// https://nodejs.org/docs/latest/api/cluster.html#clusterschedulingpolicy
cluster.schedulingPolicy = cluster.SCHED_RR;
// Ensures that the `backlog` is used to create a `net.Server`.
const kExpectedBacklog = 127;
if (cluster.isMaster) {
const listen = net.Server.prototype.listen;
net.Server.prototype.listen = common.mustCall(
function(...args) {
const options = args[0];
if (typeof options === 'object') {
assert(options.backlog, kExpectedBacklog);
} else {
assert(args[1], kExpectedBacklog);
}
return listen.call(this, ...args);
}
);
const worker = cluster.fork();
worker.on('message', () => {
worker.disconnect();
});
} else {
const server = net.createServer();
server.listen({
host: common.localhostIPv4,
port: 0,
backlog: kExpectedBacklog,
}, common.mustCall(() => {
process.send(true);
}));
}