-
-
Save aikar/735236 to your computer and use it in GitHub Desktop.
This file contains 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
<html> | |
<head></head> | |
<body> | |
<p><textarea cols=100 rows=25 id="output"></textarea></p> | |
</body> | |
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> | |
<script type="text/javascript"> | |
var client = new function() { | |
var lastid=0; | |
var _poll = function(lastid) { | |
$.get('/foo?client=1&chan=7&last='+lastid, function(response) { | |
if(response.length > 0){ | |
$('textarea').text(response); | |
lastid = JSON.parse(response).reverse()[0].id; | |
} | |
_poll(lastid); // using reverse to get the latest head ID | |
}); | |
} | |
// Inital request | |
$.get('/foo?client=1&chan=7&last='+lastid, function(response) { | |
$('textarea').text(response); | |
lastid = JSON.parse(response).reverse()[0].id; | |
_poll(lastid); | |
}); | |
} | |
</script> | |
</html> |
This file contains 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
config = module.exports = { | |
port : 8000, | |
redis: { | |
host: '127.0.0.1', | |
port: 6379 | |
}, | |
}; |
This file contains 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
/** | |
* MultiServ | |
* | |
* Enables running a TCP Socket server over multiple processes to enable using multiple cores on a system | |
* Written by Daniel Ennis (Aikar) <[email protected]> | |
* License: WTFPL | |
**/ | |
(function() | |
{ | |
var net = require('net'); | |
var self = __filename; | |
var env = process.env; | |
if(!env.__IS_CHILD) | |
{ | |
var spawn = require('child_process').spawn; | |
var path = require('path'); | |
var util = require('util'); | |
var netBinding = process.binding('net'); | |
multiserv = module.exports = function(app, numWorkers, options) | |
{ | |
var port = options.port || 80; | |
var ip = options.ip || '0.0.0.0'; | |
var workers = []; | |
var env = {}; | |
var shuttingDown = false; | |
for(var i in process.env) | |
{ | |
env[i] = process.env[i]; | |
} | |
["SIGINT", "SIGTERM", "SIGKILL", "SIGQUIT", "SIGHUP", "exit"].forEach(function(signal) | |
{ | |
shuttingDown = true; | |
process.addListener(signal, function(signal) | |
{ | |
workers.forEach(function(worker) | |
{ | |
if(signal != 'exit') worker.kill(signal); | |
worker._stdin.end(); | |
worker._stdout.end(); | |
}); | |
if(signal != 'exit') process.reallyExit(); | |
}); | |
}); | |
env.__IS_CHILD = true; | |
var i = 0; | |
var spawnWorker = function() | |
{ | |
var stdin = netBinding.socketpair(); | |
var stdout = netBinding.socketpair(); | |
var stderr = netBinding.socketpair(); | |
var worker = spawn(process.execPath, [self, app], { | |
env: env, | |
customFds: [stdin[1], stdout[1], stderr[1]] | |
}); | |
worker.sendFD = function(fd) | |
{ | |
worker._stdin.write('fd', 'utf8', fd); | |
}; | |
workers.push(worker); | |
worker.i = i; | |
worker._stdin = new net.Stream(stdin[0],'unix'); | |
worker._stdin.resume(); | |
worker._stdout = new net.Stream(stdout[0],'unix'); | |
worker._stdout.on('data', function(data) | |
{ | |
var index = workers.indexOf(worker); | |
console.log('[worker:' + worker.i + ']<< ' + data.toString().replace(/^\s*/, "").replace(/\s*$/, "") + '>>'); | |
}); | |
worker._stdout.resume(); | |
worker._stderr = new net.Stream(stderr[0],'unix'); | |
worker._stderr.on('data', function(data) | |
{ | |
var index = workers.indexOf(worker); | |
console.log('[error worker:' + worker.i + ']<< ' + data.toString().replace(/^\s*/, "").replace(/\s*$/, "") + '>>'); | |
}); | |
worker._stderr.resume(); | |
worker.on('exit', function(code) | |
{ | |
var index = workers.indexOf(worker); | |
if(!shuttingDown) console.log('worker',worker.i,' has died') | |
if(index >= 0) | |
{ | |
workers.splice(index, 1); | |
} | |
if(!code && !shuttingDown) | |
{ | |
spawnWorker(); | |
} | |
}); | |
//console.log('Workers:', workers.length, workers); | |
} | |
for(var i = 0; i < numWorkers; i++) | |
{ | |
spawnWorker(); | |
} | |
if(workers.length) | |
{ | |
var defaultHandler = function(conn, workers) | |
{ | |
if(workers.length) | |
{ | |
conn.pause(); | |
var hv = 0; | |
conn.remoteAddress.split('.').forEach(function(v) | |
{ | |
hv += parseInt(v); | |
}); | |
var i = hv % workers.length; | |
//console.log('got conn on i', i, workers.length, workers); | |
workers[i].sendFD(conn.fd); | |
}else{ | |
conn.end(); | |
} | |
} | |
var handler = options.handler || defaultHandler; | |
net.createServer(function(conn) | |
{ | |
handler(conn, workers); | |
}).listen(port, ip); | |
} | |
return workers; | |
} | |
} | |
else | |
{ | |
if(process.argv[2]) | |
{ | |
var serv = require(process.argv[2]); | |
var stdin = new net.Stream(0, 'unix'); | |
["SIGINT", "SIGTERM", "SIGKILL", "SIGQUIT", "SIGHUP", "exit"].forEach(function(signal) | |
{ | |
process.addListener(signal, function() | |
{ | |
stdin.end(); | |
if(signal != 'exit') process.reallyExit(); | |
}); | |
}); | |
stdin.on('data', function(data){ | |
//console.log('client got data', data); | |
}); | |
stdin.on('fd', function(fd) | |
{ | |
//console.log('got fd'); | |
var client = new net.Stream(fd, 'unix'); | |
client.type = serv.type; | |
client.server = serv; | |
["SIGINT", "SIGTERM", "SIGKILL", "SIGQUIT", "SIGHUP", "exit"].forEach(function(signal) | |
{ | |
process.addListener(signal, function() | |
{ | |
client.end(); | |
}); | |
}); | |
client.resume(); | |
serv.emit('connection', client); | |
}); | |
stdin.resume(); | |
} | |
} | |
})(); | |
This file contains 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
var conf = require('./config'), | |
multiserv = require('./multiserv'); | |
multiserv(require.resolve('./toyservernode'), 8, {port: conf.port}); |
This file contains 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
(function(){ | |
var http = require('http'), | |
url = require('url'), | |
fs = require('fs'), | |
conf = require('./config'), | |
redis = require('redis'); | |
var Chan = function(id){ | |
var pubsub = redis.createClient(conf['redis']['port'], conf['redis']['host']); | |
var db = redis.createClient(conf['redis']['port'], conf['redis']['host']); | |
// A note on IDs. I use incrementing ints in the toy, but uuids in the syncpad server | |
var ID = 0; | |
var alterObj = function(obj){ | |
if(obj.eggs){ | |
delete obj.spam; | |
} | |
return obj; | |
}; | |
var pool = []; | |
pubsub.on('message', function(channel, record){ | |
var remove = []; | |
pool.forEach(function(val){ | |
var obj = JSON.parse(record); | |
if(val.clientId != obj.client){ | |
obj = alterObj(obj); | |
// now back to a string for the client | |
val.response.write(JSON.stringify([obj])); | |
remove.push(val); | |
val.response.end(); | |
} | |
}); | |
remove.forEach(function(val) | |
{ | |
var idx = pool.indexOf(val); | |
if(idx >= 0) pool.splice(idx, 1); | |
}); | |
}); | |
var startClient = function(response, clientId){ | |
var entry = { | |
clientId:clientId, response:response | |
}; | |
pool.push(entry); | |
setTimeout(function() { | |
var idx = pool.indexOf(entry); | |
if(idx >= 0) pool.splice(idx, 1); | |
response.end(); | |
}, 15000); | |
} | |
pubsub.subscribe(id); | |
return { | |
id:id, | |
foo: function(last, clientId, response){ | |
db.lrange(id, 0, 500, function(err, records){ | |
try{ | |
if(records != null){ | |
// In the syncpad server code, this actually maps 1000 elements, which could account for better performance in the toy server | |
var l = records.map(JSON.parse); | |
var curr = l[0].id; | |
if (last != curr){ | |
var objList = new Array(); | |
for (var i=0; i<l.length; i++){ | |
if(last == l[i].id){ | |
objList = objList.map(alterObj); | |
response.write(JSON.stringify(objList)); | |
break; | |
} else { | |
objList.unshift(l[i]); | |
} | |
} | |
response.end(); | |
} else { | |
startClient(response, clientId); | |
} | |
} else { | |
startClient(response,clientId); | |
} | |
} catch(error){ | |
response.end(); | |
} | |
}); | |
return; | |
}, | |
bar: function(query, response){ | |
try{ | |
var obj = { | |
id: ID++, | |
foo: query.foo, | |
bar: query.bar, | |
baz: query.baz, | |
spam: query.spam, | |
eggs: query.eggs, | |
client: query.client | |
}; | |
var value = JSON.stringify(obj); | |
db.publish(id, value); | |
db.lpush(id, value); | |
response.writeHead(201, {'Content-Type': 'text/plain'}); | |
}catch(error){ | |
response.writeHead(400, {'Content-Type': 'text/plain'}); | |
} | |
response.end(); | |
return; | |
}, | |
close: function(){ | |
db.end(); | |
pubsub.unsubscribe(); | |
pubsub.end(); | |
return; | |
} | |
} | |
} | |
var ToyServer = function(){ | |
var _self = this; | |
var chans = {}; | |
var handleFoo = function(req, res){ | |
// sample relative URI : "/foo?chan=7&client=1&last=4" | |
var query = url.parse(req.url,true).query; | |
var last = query.last; | |
var id = query.chan; | |
var clientId = query.client; | |
if (chans[id] === undefined){ | |
chans[id] = new Chan(id) | |
} | |
chan = chans[id]; | |
res.writeHead(200, {'Content-Type': 'text/plain'}); | |
chan.foo(last, clientId, res); | |
}; | |
var handleBar = function(req, res){ | |
// sample relative URI "/bar?foo=1&bar=2&baz=3&spam=4&eggs=5&client=6&chan=7" | |
var query = url.parse(req.url,true).query; | |
var id = query.chan; | |
if (chans[id] === undefined){ | |
chans[id] = new Chan(id) | |
} | |
chan = chans[id]; | |
chan.bar(query, res); | |
}; | |
var routes = { | |
'/foo': handleFoo, | |
'/bar': handleBar | |
}; | |
var _requestHandler = function(request, response) { | |
if(routes[url.parse(request.url).pathname] === undefined) { | |
response.writeHead(404, {'Content-Type': 'text/plain'}); | |
response.write('HTTP 404: Not Found\n'); | |
response.end(); | |
return; | |
} else { | |
routes[url.parse(request.url).pathname].call(this, request, response); | |
} | |
}; | |
module.exports = http.createServer().addListener('request', _requestHandler) | |
}; | |
new ToyServer(); | |
})(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment