Im building a nodejs/socket.io based game and Im trying to implement node clustering to take advantage on multicore machines ( few machines, each has few cores ). I figured out that memcache will be nice solution, but Im not completely sure if it'll survive high load, because each game will do about 50 write/read per second. Also what will be the best solution to broadcast message to all clients while they're connected to different servers. For example player X is connected to node 1, he do a simple action and how I can broqdcast the action to player Y which is connected to node 2.
If you are going to be clustering across threads, then using Redis as your Socket.IO store is a perfect solution.
Redis is an extremely fast database, entirely run from memory, that can support thousands of publish/subscribe messages in a single second. Socket.IO has built-in support for Redis and when using it, any message emitted from one instance of Socket.IO is published to Redis, and all Socket.IO instances that have subscribed to the same Redis instance will also emit the same message.
This is how you would set up each Socket.IO instance:
var RedisStore = require('socket.io/lib/stores/redis');
var redis = require('socket.io/node_modules/redis')
io.set('store', new RedisStore({
redisPub: redis.createClient(),
redisSub: redis.createClient(),
RedisClient: redis.createClient()
}));
When Socket.IO is set up like this, and you have a Redis server, a simple io.sockets.emit() will broadcast to all clients on any server regardless of which server executed the code if all servers are publishing/subscribing to the Redis instance.
You can actually serialize the object into a JSON string and pass it as argument and de-serialize at each worker (num of CPUs).
Otherwise you can use cluster.worker[<id>].send(<object>) which will automatically take care of serializing/de-serializing.
For more information: Check Node API docs
Related
I have an existing application of node.js and socket.io with forever . But now I would like to use, pm2 cluster module instead of forever. But I have been facing some difficulties with socket.io and the cluster instances., as at few places the message is lost. So I read a little online to use another module called socket.io-with-pm2-cluster. Which acts as a plugin. But while using it., it asks me to configure in a way that each instance will have to listen to different ports. Like if the app is running on port 3000, the instance 0,1,23 will have to use 3001,3002,3003,3004. Can anyone suggest if it is the right approach ? Or any other work around s to make this possible ?
I will recommend using socket.io-redis for this purpose which is recommended approach by socket.io. So if you scale to multiple computers in future this will work fine as expected but in the current approach may be its failed to work on multiple computers like in case of AWS, in that case, you can also use a sticky session of LB but this.
socket.io needs to keep the socket open to get events from the server back to the client (and vice-versa) and you are running multiple workers so that is why you getting "few places the message is lost".
Sticky load balancing
If you plan to distribute the load of connections among different
processes or machines, you have to make sure that requests associated
with a particular session id connect to the process that originated
them.
you need to introduce layer that make your service stateless, you can use socket.io-redis
By running socket.io with the socket.io-redis adapter you can run
multiple socket.io instances in different processes or servers that
can all broadcast and emit events to and from each other.
Passing events between nodes
you want to have multiple Socket.IO nodes accepting connections, if you want to broadcast events to everyone (or even everyone in a certain room) you’ll need some way of passing messages between processes or computers.
The interface in charge of routing messages is what we call the Adapter. You can implement your own on top of the socket.io-adapter (by inheriting from it) or you can use the one we provide on top of Redis: socket.io-redis:
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
Then the following call:
io.emit('hi', 'all sockets');
will be broadcast to every node through the Pub/Sub mechanism of Redis.
You can read further details here
My current setup is running multiple node instances using PM2 to manage the instances and act as a load balancer.
I would like to implement some functionality using websockets.
The first issue that came to mind is sharing the sockets among X node instances.
My understanding is that if I boot up a websocket-server within a node env only that env will have access to the sockets connected to it.
I do not want to load up web sockets for each instance for each user as that seems like a waste of resources.
Currently I am playing around with the websocket package on npm but I am in no way tied to this if there is a better alternative.
I would like the sockets to more or less push data one-way from server to client and avoid anything coming from the client to the server.
My solution so far is spin up another node instance that solely acts as a websocket server.
This would allow a user to make all requests as normal to the usual instances but make a websocket connection to the separate node instance dedicated to sockets.
The servee could then fire off messages to the dedicated socket server anytime something is updated to send data back to the appropriate clients.
I am not sure this is the best option and I am trying to see if there are other recommended ways of managing websockets across multiple node instances yet still allow me to spin up/down node instances as required.
I'd recommend you avoid a complex setup and just get socket.io working across multiple nodes, thus distributing the load; If you want to avoid data coming from the client to the server, just don't listen to incoming events on your server.
Socket.io supports multiple nodes, under the following conditions:
You have sticky sessions enabled. This ensures requests connect back to the process from which they originated from.
You use a special adapter called socket.io-redis & a small Redis instance as a central point of storage - it keeps track of namespaces/rooms and connected sockets across your cluster of nodes.
Here's an example:
// setup io as usual
const io = require('socket.io')(3000)
// Set a redisAdapter as an adapter.
const redisAdapter = require('socket.io-redis')
io.adapter(redisAdapter({ host: 'localhost', port: 6379 }))
From then on, it's business as usual:
io.emit('hello', 'to all clients')
You can read more here: Socket.IO - Using Multiple Nodes.
From the socket.io docs [http://socket.io/docs/rooms-and-namespaces/#sending-messages-from-the-outside-world] I read the following but I can't seem to connect it to any use case in my head:
Sending messages from the outside-world In some cases, you might want
to emit events to sockets in Socket.IO namespaces / rooms from outside
the context of your Socket.IO processes.
There’s several ways to tackle this problem, like implementing your
own channel to send messages into the process.
To facilitate this use case, we created two modules:
socket.io-redis
socket.io-emitter
By implementing the Redis Adapter:
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
you can then emit messages from any other process to any channel
var io = require('socket.io-emitter')();
setInterval(function(){
io.emit('time', new Date);
}, 5000);
If you have a cluster of servers and want to talk to clients that are connected to different instances, you'll need a common storage -- that's when you use Redis.
You are also mentioning io-emitter, which is a way for other processes to post messages to your clients. For example, if a worker needs to emit messages to your clients, it can use io-emitter. Redis is the common glue for sharing messages across different processes/servers.
The module is needed only when you want to spread your solution to multiple solutions or node processes. Through the redis adapter the multiple servers could broadcast to other clients.
Basically when you have two servers each running their own server. Server A has three clients. Server B has two different clients. These two servers does not share any client information so you won't be able to broadcast to all the users message. The adapter gives you ability to connect these different servers into one(using redis), so you would be able to broadcast to all the users.
Also good presentation to look about socket.io and redis http://www.slideshare.net/YorkTsai/jsdc2013-28389880.
I am developing a little project using Node.js. I am using mongoose for models, therefore i am using MongoDb. And i keep sessions in MongoStore. Also i want to use socket.io running several processes of Node. From socket.io docs:
The MemoryStore only allows you deploy socket.io on a single process.
If you want to scale to multiple process and / or multiple servers
you can use our RedisStore which uses the Redis
NoSQL database as man in the middle.
So i think i need Redis too. I am new in Node and i want to know - is it normal to use two databases to manage different parts of application. Or is there a way to work with socket.io when running several processes of Node and use only MongoDb
Just recently a solution that uses MongoStore with pub/sub functionality using mubsub (Pub/sub for Node.js and MongoDB) has appeared.
It can be attached to socket.io in almost the same way as you would with RedisStore:
io.configure(function() {
io.set('store', new MongoStore({host: 'localhost', port: 27017, db:'session_db'}));
});
More information and source at: https://github.com/kof/socket.io-mongo
The Redis store is already built into Socket.IO, but more importantly has 2 important features that are particularly needed for Socket.IO:
1) Publish-subscribe (to communicate between processes)
2) Key-value store (to store all the info about connections)
While the key-value store part can be done with MongoDB, it doesn't provide the pub-sub functionality.
Bottom line, IF you need to scale beyond one process (meaning you are expecting more than some thousand concurrent request) than RedisStore is the solution.
Resources:
Examples in using RedisStore in socket.io
http://www.ranu.com.ar/2011/11/redisstore-and-rooms-with-socketio.html
I am trying to scale a simple socket.io app across multiple processes and/or servers.
Socket.io supports RedisStore but I'm confused as to how to use it.
I'm looking at this example,
http://www.ranu.com.ar/post/50418940422/redisstore-and-rooms-with-socket-io
but I don't understand how using RedisStore in that code would be any different from using MemoryStore. Can someone explain it to me?
Also what is difference between configuring socket.io to use redisstore vs. creating your own redis client and set/get your own data?
I'm new to node.js, socket.io and redis so please point out if I missed something obvious.
but I don't understand how using RedisStore in that code would be any different from using MemoryStore. Can someone explain it to me?
The difference is that when using the default MemoryStore, any message that you emit in a worker will only be sent to clients connected to the same worker, since there is no IPC between the workers. Using the RedisStore, your message will be published to a redis server, which all your workers are subscribing to. Thus, the message will be picked up and broadcast by all workers, and all connected clients.
Also what is difference between configuring socket.io to use redisstore vs. creating your own redis client and set/get your own data?
I'm not intimately familiar with RedisStore, and so I'm not sure about all differences. But doing it yourself would be a perfectly valid practice. In that case, you could publish all messages to a redis server, and listen to those in your socket handler. It would probably be more work for you, but you would also have more control over how you want to set it up. I've done something similar myself:
// Publishing a message somewhere
var pub = redis.createClient();
pub.publish("messages", JSON.stringify({type: "foo", content: "bar"}));
// Socket handler
io.sockets.on("connection", function(socket) {
var sub = redis.createClient();
sub.subscribe("messages");
sub.on("message", function(channel, message) {
socket.send(message);
});
socket.on("disconnect", function() {
sub.unsubscribe("messages");
sub.quit();
});
});
This also means you have to take care of more advanced message routing yourself, for instance by publishing/subscribing to different channels. With RedisStore, you get that functionality for free by using socket.io channels (io.sockets.of("channel").emit(...)).
A potentially big drawback with this is that socket.io sessions are not shared between workers. This will probably mean problems if you use any of the long-polling transports.
I set up a small github project to use redis as datastore.
Now you can run multiple socket.io server processes.
https://github.com/markap/socket.io-scale
Also what is difference between configuring socket.io to use redisstore vs. creating your own redis client and set/get your own data?
The difference is that, when you use 'RedisStore', the socket.io itself will save the socket heartbeat and session info into the Redis, and if you use cluster with node.js, the user client can work.
Without redis, the client might change the node.js process next time, so the session will be lost.
The difference is, if you have a cluster of node.js instances running, memStore won't work since it's only visible to a single process.