Gracefully handle EMFILE
Implementing a tip from Marc Lehmann: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#The_special_problem_of_accept_ing_wh Keep an extra FD around for every server. When you hit EMFILE, destroy that FD, accept a connection, close it; in this way you can clear the connection queue and let people know that you're overload. No more timeout needed.
This commit is contained in:
parent
79ecc8e9b7
commit
ac54272218
@ -17,7 +17,7 @@ function connect () {
|
|||||||
|
|
||||||
s.on('close', function () {
|
s.on('close', function () {
|
||||||
if (gotConnected) connections--;
|
if (gotConnected) connections--;
|
||||||
if (!haderror) connect();
|
//if (!haderror) connect();
|
||||||
});
|
});
|
||||||
|
|
||||||
s.on('end', function () {
|
s.on('end', function () {
|
||||||
@ -36,6 +36,9 @@ connect();
|
|||||||
|
|
||||||
var oldConnections, oldErrors;
|
var oldConnections, oldErrors;
|
||||||
|
|
||||||
|
// Try to start new connections every so often
|
||||||
|
setInterval(connect, 5000);
|
||||||
|
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
if (oldConnections != connections) {
|
if (oldConnections != connections) {
|
||||||
oldConnections = connections;
|
oldConnections = connections;
|
||||||
|
61
lib/net.js
61
lib/net.js
@ -922,36 +922,38 @@ function Server (/* [ options, ] listener */) {
|
|||||||
|
|
||||||
self.connections = 0;
|
self.connections = 0;
|
||||||
|
|
||||||
self.paused = false;
|
|
||||||
self.pauseTimeout = 1000;
|
|
||||||
|
|
||||||
self.allowHalfOpen = options.allowHalfOpen || false;
|
self.allowHalfOpen = options.allowHalfOpen || false;
|
||||||
|
|
||||||
function pause () {
|
|
||||||
// We've hit the maximum file limit. What to do?
|
|
||||||
// Let's try again in 1 second.
|
|
||||||
self.watcher.stop();
|
|
||||||
|
|
||||||
// If we're already paused, don't do another timeout.
|
|
||||||
if (self.paused) return;
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
self.paused = false;
|
|
||||||
// Make sure we haven't closed in the interim
|
|
||||||
if (typeof self.fd != 'number') return;
|
|
||||||
self.watcher.start();
|
|
||||||
}, self.pauseTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.watcher = new IOWatcher();
|
self.watcher = new IOWatcher();
|
||||||
self.watcher.host = self;
|
self.watcher.host = self;
|
||||||
self.watcher.callback = function () {
|
self.watcher.callback = function () {
|
||||||
|
// Just in case we don't have a dummy fd.
|
||||||
|
if (!self._dummyFD) self._getDummyFD();
|
||||||
|
|
||||||
while (self.fd) {
|
while (self.fd) {
|
||||||
try {
|
try {
|
||||||
var peerInfo = accept(self.fd);
|
var peerInfo = accept(self.fd);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.errno == EMFILE) {
|
if (e.errno == EMFILE) {
|
||||||
pause();
|
// Output a warning, but only at most every 5 seconds.
|
||||||
|
var now = new Date();
|
||||||
|
if (now - self._lastEMFILEWarning > 5000) {
|
||||||
|
console.error("(node) Hit max file limit. Increase 'ulimit -n'.");
|
||||||
|
}
|
||||||
|
self._lastEMFILEWarning = now;
|
||||||
|
|
||||||
|
// Gracefully reject pending clients by freeing up a file
|
||||||
|
// descriptor.
|
||||||
|
if (self._dummyFD) {
|
||||||
|
close(self._dummyFD);
|
||||||
|
self._dummyFD = null;
|
||||||
|
while (true) {
|
||||||
|
peerInfo = accept(self.fd);
|
||||||
|
if (!peerInfo) break;
|
||||||
|
close(peerInfo.fd);
|
||||||
|
}
|
||||||
|
self._getDummyFD();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
@ -1076,9 +1078,23 @@ Server.prototype._startWatcher = function () {
|
|||||||
this.emit("listening");
|
this.emit("listening");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Server.prototype._getDummyFD = function () {
|
||||||
|
try {
|
||||||
|
this._dummyFD = socket("tcp");
|
||||||
|
} catch (e) {
|
||||||
|
this._dummyFD = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Server.prototype._doListen = function () {
|
Server.prototype._doListen = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
// Grab a dummy fd for EMFILE conditions.
|
||||||
|
self._getDummyFD();
|
||||||
|
self._lastEMFILEWarning = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bind(self.fd, arguments[0], arguments[1]);
|
bind(self.fd, arguments[0], arguments[1]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -1120,6 +1136,11 @@ Server.prototype.close = function () {
|
|||||||
close(self.fd);
|
close(self.fd);
|
||||||
self.fd = null;
|
self.fd = null;
|
||||||
|
|
||||||
|
if (this._dummyFD) {
|
||||||
|
close(this._dummyFD);
|
||||||
|
this._dummyFD = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (self.type === "unix") {
|
if (self.type === "unix") {
|
||||||
fs.unlink(self.path, function () {
|
fs.unlink(self.path, function () {
|
||||||
self.emit("close");
|
self.emit("close");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user