-
-
Save soletan/701fefc7727c2bc7c170 to your computer and use it in GitHub Desktop.
// 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(); | |
} ); |
Shutdown works instantly unless having processed any request. It takes a time if shutdown was initiated shortly after having processed requests.
Tested behaviour using this script ...
var http = require( "http" );
var app = require( "express" )();
app.get( "/", function( req, res ) {
req.connection.ref();
res.end( "Ho!", function() {
req.connection.unref();
} );
} );
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" );
if ( !--c ) {
process.exit();
}
} );
server2.on( "close", function _onServersClosed() {
console.log( "listener 2 closed" );
if ( !--c ) {
process.exit();
}
} );
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" );
} );
}
Test includes fetching from port 8080 several times. Instant output on pressing Ctrl+C in console was:
service got SIGINT/SIGTERM, shutting down
shut-down initiated
listener 2 closed
exiting
Basically, it looks like success, but listener 1 - the used one - didn't get close event before exiting application.
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.
Shut down is performed like this:
Code has been tested on Win32 platform.