socket.io identity playground
Experiment with ability to fetch room users with user IDs in a horizontally scalable, multi-node socket.io setup
# Install deps:
$ npm install
# Install PM2, we need to run socket.io servers in cluster mode:
$ npm install pm2@latest -g
Requirements before running:
# Migrate & seed SQLite3 DB:
$ npm run reset-db-dev
# Run socket.io server as a 4 node cluster using PM2 and stream logs:
$ npm run stop-dev && npm run start-dev && pm2 logs --merge-logs
# Stop socket.io server (kill all nodes):
$ npm run stop-dev
# Reset database and restart cluster:
$ npm run reset-db-dev && npm run stop-dev && npm run start-dev && pm2 logs --merge-logs
then visit:
https://localhost:5009/ui/Maths?id_user=1
which will create a socket.io client as id_user: '1'
and join room "Maths"
for example.
This service is also running on Heroku, available at: https://socket-identity-playground.herokuapp.com/
Pushing to main
branch triggers a Heroku build.
# Migrate/seed Heroku DB
$ heroku run npm run reset-db-staging -a socket-identity-playground
You must use one of the following id_user
when visiting a room:
id_user | name |
---|---|
1 | John |
2 | Mary |
3 | Mike |
4 | Frank |
5 | Stephanie |
6 | Richard |
7 | Sonia |
8 | Charlie |
9 | Rowan |
10 | Victoria |
You can join any room you want.
The client passes an id_user
when connecting with a node.
The node, saves that value as a property of the socket, like so:
// server
io.on('connection', socket => {
socket.id_user = socket.handshake.query.id_user
})
The service exposes a REST endpoint, GET: /:room/users
.
When pinged:
GET
request lands on a node, we'll call it the request node.- The request node then finds out all the
socket ids
for that room. - For each
id_socket
:- Asks all other nodes if they have in their state a
socket
with thatid_socket
. If yes, the other node responds with thesocket.id_user
of the foundsocket
.
- Asks all other nodes if they have in their state a
- The request node responds to the
GET
request with a user list, each user containing aid_socket
and anid_user
like so:
// Room "Maths":
[
{ id_socket: '70tSKzb9gzWwfL', id_user: 'foo' },
{ id_socket: '62sGlHCfUDdmAA', id_user: 'bar' }
]
- The client is then responsible for hydrating the
id_user
s with the full user objects. - When a client updates his identity info, he then broadcasts an
identity-change
event. - Each participant in the room then rehydrates the user object for the
id_user
that emitted theidentity-change
.
- We don't create additional state. Each node already has a state, the
io
object which keeps track of all connectedsockets
to that node. We simply add an additional property,id_user
to eachsocket
. - Nodes do not actually directly communicate with each other.
Communication between nodes happens using the
customRequest
mechanism, which uses Redis Pub/Sub.
The Profs
The MIT License