Created
September 27, 2015 14:40
-
-
Save soletan/701fefc7727c2bc7c170 to your computer and use it in GitHub Desktop.
How to shut down express js gracefully while supporting keep-alive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this is an excerpt of essential but non-working part extracted from local expressjs application ... | |
var app = require( "express" )(); | |
var httpServer = require( "http" ).createServer( app ); | |
httpServer.on( "connection", function( socket ) { | |
socket.unref(); | |
} ); | |
httpServer.listen( CONFIG.port, CONFIG.ip ); | |
app.use( function _gracefulShutdownSupporter( req, res, next ) { | |
// mark connection to be actively serving request now | |
req.connection.ref(); | |
// wrap genuine end() on response to clear mark afterwards | |
res._genuineEnd = res.end; | |
res.end = function() { | |
var args = [], i, l, a, injected = false, | |
socket = req.connection; | |
for ( i = 0, l = arguments.length, args = []; i < l; i++ ) { | |
a = arguments[i]; | |
if ( typeof a === "function" && !injected ) { | |
injected = true; | |
args[i] = (function( genuine ) { | |
return function() { | |
socket.unref(); | |
return genuine.apply( this, arguments ); | |
}; | |
})( a ); | |
} else { | |
args[i] = a; | |
} | |
} | |
if ( !injected ) { | |
args.push( function() { | |
socket.unref(); | |
} ); | |
} | |
return res._genuineEnd.apply( this, args ); | |
}; | |
next(); | |
} ); |
Obviously, server is behaving differently (e.g. by not emitting "close" event) in case of using ref()/unref() on sockets.
Relying on a separate collection of used sockets and tracking their activity is working though:
var http = require( "http" );
var app = require( "express" )();
var sockets = {}, nextId = 1;
app.get( "/", function( req, res ) {
var socket = req.connection,
id = socket._socketsQueueId;
if ( !id ) {
id = socket._socketsQueueId = nextId++;
sockets[id] = socket;
socket.on( "close", function() {
sockets[id] = undefined;
} );
}
socket._currentlyActive = true;
res.end( "Ho!", function() {
socket._currentlyActive = false;
} );
} );
var server = http.createServer( app );
server.listen( 8080 );
var server2 = http.createServer( app );
server2.listen( 8081 );
function _shutdownService() {
var c = 2;
console.log( "service got SIGINT/SIGTERM, shutting down" );
process.on( "beforeExit", function() { console.log( "exiting" ); } );
server.on( "close", function _onServersClosed() {
console.log( "listener 1 closed, %d left", --c );
if ( c === 0 ) {
console.log( "trigger exit" );
process.exit();
}
} );
server2.on( "close", function _onServersClosed() {
console.log( "listener 2 closed, %d left", --c );
if ( c === 0 ) {
console.log( "trigger exit" );
process.exit();
}
} );
Object.keys( sockets )
.forEach( function( id ) {
var socket = sockets[id];
if ( socket && !socket._currentlyActive ) {
socket.setTimeout( 100, function() {
socket.end();
socket.destroy();
} );
}
} );
server.close();
server2.close();
console.log( "shut-down initiated" );
}
process.on( "SIGINT", _shutdownService );
if ( process.platform === "win32" ) {
var rl = require( "readline" ).createInterface( {
input: process.stdin,
output: process.stdout
} );
rl.on( "SIGINT", function() {
rl.close();
process.emit( "SIGINT" );
} );
}
Though requesting on either port 8080 and 8081, the service stops instantly on pressing Ctrl+C in Windows terminal writing:
service got SIGINT/SIGTERM, shutting down
shut-down initiated
listener 2 closed, 1 left
listener 1 closed, 0 left
trigger exit
Will try to inject this solution into my initial application now.
As a final note, gracefully shutting down expressjs application (involving database connection) was working finally using the latest approach given above.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Tested behaviour using this script ...
Test includes fetching from port 8080 several times. Instant output on pressing Ctrl+C in console was:
Basically, it looks like success, but listener 1 - the used one - didn't get close event before exiting application.