Skip to content

Instantly share code, notes, and snippets.

@meltzerj
Created May 30, 2013 01:47
Show Gist options
  • Save meltzerj/5675274 to your computer and use it in GitHub Desktop.
Save meltzerj/5675274 to your computer and use it in GitHub Desktop.
var net = require('net'),
http = require('http'),
cluster = require('cluster');
function hash(ip, seed) {
var hash = ip.reduce(function(r, num) {
r += parseInt(num, 10);
r %= 2147483648;
r += (r << 10)
r %= 2147483648;
r ^= r >> 6;
return r;
}, seed);
hash += hash << 3;
hash %= 2147483648;
hash ^= hash >> 11;
hash += hash << 15;
hash %= 2147483648;
return hash >>> 0;
}
module.exports = function sticky(num, callback) {
var server;
// `num` argument is optional
if (typeof num !== 'number') {
callback = num;
num = require('os').cpus().length;
}
// Master will spawn `num` workers
if (cluster.isMaster) {
var workers = [];
for (var i = 0; i < num; i++) {
!function spawn(i) {
workers[i] = cluster.fork();
// Restart worker on exit
workers[i].on('exit', function() {
console.log('sticky-session: worker died');
spawn(i);
});
}(i);
}
var seed = ~~(Math.random() * 1e9);
server = net.createServer(function(c) {
// Get int31 hash of ip
var worker,
ipHash = hash((c.remoteAddress || '').split(/\./g), seed);
// Pause socket (so we won't loose any data)
c.pause();
// Pass connection to worker
worker = workers[ipHash % workers.length];
worker.send('sticky-session:connection', c._handle);
// And detach socket from master process
c._handle.close();
});
}
} else {
server = typeof callback === 'function' ? callback() : callback;
// Worker process
process.on('message', function(msg, handle) {
if (msg !== 'sticky-session:connection') return;
var socket = new net.Socket({ handle: handle });
// Socket is non-writable by default
socket.readable = socket.writable = true;
// instead of passing in a node server object, I passed in my own object with the node server as the handle attribute
server.handle.emit('connection', socket);
// Unpause it
socket.pause();
socket.resume();
});
if (!server) throw new Error('Worker hasn\'t created server!');
// Monkey patch server to do not bind to port
var oldListen = server.handle.listen;
server.handle.listen = function listen() {
var lastArg = arguments[arguments.length - 1];
if (typeof lastArg === 'function') lastArg();
return oldListen.call(this, null);
};
}
return server;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment