SocketCluster
SocketCluster is a WebSocket server cluster (with HTTP long-polling fallback) based on engine.io.
Unlike other realtime engines, SocketCluster deploys itself as a cluster in order to make use of all CPUs/cores on
a machine/instance - This offers a more consistent performance for users and lets you scale vertically without limits.
SocketCluster workers are highly parallelized - Asymptotically speaking, SocketCluster is N times faster than any other
available WebSocket server (where N is the number of CPUs/cores available on your machine).
SocketCluster was designed to be lightweight and its API is almost identical to Socket.io.
Other advantages of SocketCluster include:
- Sockets which are bound to the same browser (for example, across multiple tabs) share the same session.
- You can emit an event on a session to notify all sockets that belong to it.
- The SocketCluster client (socketcluster-client) has an option to allow disconnected sockets to automatically (and seamlessly) reconnect
if they lose the connection.
- Server crashes are transparent to users (aside from a 2 to 5 second delay to allow the worker to respawn) - Session data remains intact between crashes.
- It uses a memory store cluster called nData which you can use to store 'volatile' session data which relates to your sockets/sessions.
To install, run:
npm install socketcluster
Note that to use socketcluster you will also need the client which you an get using the following command:
npm install socketcluster-client
The socketcluster-client script is called socketcluster.js (located in the main socketcluster-client directory)
How to use
The following example launches SocketCluster as 7 distinct processes (in addition to the current master process):
- 3 workers on ports 9100, 9101, 9102
- 3 stores on ports 9001, 9002, 9003
- 1 load balancer on port 8000 which distributes requests evenly between the 3 workers
var SocketCluster = require('socketcluster').SocketCluster;
var socketCluster = new SocketCluster({
workers: [9100, 9101, 9102],
stores: [9001, 9002, 9003],
balancerCount: 1,
port: 8000,
appName: 'myapp',
workerController: 'worker.js'
});
The appName option can be any string which uniquely identifies this application.
This avoids potential issues with having multiple SocketCluster apps run under the same domain - It is
used internally for various purposes.
The workerController option is the path to a file which each SocketCluster worker will use to bootstrap itself.
This file is a standard Node.js module which must expose a run() function - Inside this run function is where you should
put all your application logic.
Example 'worker.js':
var fs = require('fs');
module.exports.run = function (worker) {
var httpServer = worker.getHTTPServer();
var wsServer = worker.getWSServer();
var htmlPath = __dirname + '/index.html';
var clientPath = __dirname + '/node_modules/socketcluster-client/socketcluster.js';
var html = fs.readFileSync(htmlPath, {
encoding: 'utf8'
});
var clientCode = fs.readFileSync(clientPath, {
encoding: 'utf8'
});
httpServer.on('req', function (req, res) {
if (req.url == '/socketcluster.js') {
res.writeHead(200, {
'Content-Type': 'text/javascript'
});
res.end(clientCode);
} else if (req.url == '/') {
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.end(html);
}
});
var activeSessions = {};
wsServer.on('connection', function (socket) {
socket.emit('greet', 'hello world')
activeSessions[socket.session.id] = socket.session;
});
wsServer.on('disconnection', function (socket) {
console.log('Socket ' + socket.id + ' was disconnected');
});
wsServer.on('sessiondestroy', function (ssid) {
delete activeSessions[ssid];
});
setInterval(function () {
for (var i in activeSessions) {
activeSessions[i].emit('rand', Math.floor(Math.random() * 100));
}
}, 1000);
};
Using with Express
Using SocketCluster with express is simple, you put the code inside your workerController:
module.exports.run = function (worker) {
var httpServer = worker.getHTTPServer();
var wsServer = worker.getWSServer();
var app = require('express')();
httpServer.on('req', app);
}
Using over HTTPS
In order to run SocketCluster over HTTPS, all you need to do is set the protocol to 'https' and
provide your private key and certificate as a start option when you instantiate SocketCluster - Example:
var socketCluster = new SocketCluster({
workers: [9100, 9101, 9102],
stores: [9001, 9002, 9003],
balancerCount: 1,
port: 8000,
appName: 'myapp',
workerController: 'worker.js',
protocol: 'https',
protocolOptions: {
key: fs.readFileSync(__dirname + '/keys/enc_key.pem', 'utf8'),
cert: fs.readFileSync(__dirname + '/keys/cert.pem', 'utf8'),
passphrase: 'passphase4privkey'
}
});
The protocolOptions option is exactly the same as the one you pass to a standard Node HTTPS server:
http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
Note that encryption/decryption in SocketCluster happens at the LoadBalancer level (SocketCluster launches one or more
lightweight load balancers to distribute traffic evenly between your SocketCluster workers).
LoadBalancers are responsible for encrypting/decrypting all network traffic. What this means is that your code (which is in the worker layer)
will only ever deal with raw HTTP traffic.
Authentication
SocketCluster lets you store session data using the socket.session object.
This object gives you access to a cluster of in-memory stores called nData.
You can effectively invoke any of the methods documented here to store and retrieve session data:
https://github.com/topcloud/ndata
For example, to authorize a user, you could check their login credentials and upon
success, you could add an auth token to that session:
socket.session.set('isUserAuthorized', true, callback);
Then, on subsequent events, you could check for that token before handling the event:
socket.session.get('isUserAuthorized', function (err, value) {
if (value) {
}
});
The session object can also be accessed from the req object that you get from
SocketCluster's HTTP server 'req' event (I.e. req.session).
Contribute to SocketCluster
- Tests needed - While some of the underlying modules of SC are well tested,
it would be nice to add some higher-level tests to help maintain high code quality.
- Benchmarks - It would be nice to have some formal benchmarks comparing SocketCluster's performance with alternatives to
get a better view of its strengths and weaknesses. Also having some graphs to prove that SocketCluster scales linearly as
you add more workers/CPUs would be nice. This has been verified informally with a few CPU cores, but more thorough testing would be welcome!
- Speed - More speed is always better!
API (Documentation coming soon)
SocketCluster
Exposed by require('socketcluster').SocketCluster
.
SocketCluster(opts:Object)
Creates a new SocketCluster, must be invoked with the new keyword.
var SocketCluster = require('socketcluster').SocketCluster;
var socketCluster = new SocketCluster({
workers: [9100, 9101, 9102],
stores: [9001, 9002, 9003],
port: 8000,
appName: 'myapp',
workerController: 'worker.js'
});
Documentation on all supported options is coming soon (there are around 30 of them - Most of them are optional).
SocketWorker
A SocketWorker object is passed as the argument to your workerController's run(worker) function.
Example - Inside worker.js:
module.exports.run = function (worker) {
}
ClusterServer
A ClusterServer instance is returned from worker.getWSServer() - You use it to handle WebSocket connections.