Why Websockets?
Limitations Of HTTP
With HTTP, client requests a resource and the server responds with the requested data. It is a unidirectional communication - the data must be first requested by the client. A workaround for this limitation was the HTTP long polling. With long polling, client makes a http request with a long timeout period and the server uses that time period to keep sending data to the client. But long polling had its own overhead with timeouts, latency and headers being sent in every request.
The Websocket Protocol
WebSocket protocol is designed to provide a full-duplex communication over a single TCP connection. The protocol consists of an opening handshake followed by basic message framing, layered over TCP. WebSockets begin life as a standard HTTP request and response. Within that request response chain, the client asks to open a WebSocket connection, and the server responds

- The Client initiates a handshake by sending a HTTP request to the server with the header containing
Upgrade: websocket
andConnection: Upgrade
to switch to websocket protocol along with other information. - If the server can establish a connection successfully, it responds back with the header containing status code 101, which depicts switching protocol.
- The websocket connection is now open and the client and server can start emitting messages.
- The connection will be closed when either the server or client closes the connection.
What Is SocketIO? How Does It Work?
SocketIO is not a websocket implementation, but a library that provides real time bidirectional and event based communication. It consists of a server and a client that uses the websocket protocol to transmit event messages between the client and server.
SocketIO uses long polling and websockets as its transport. It starts with http long polling by default and then upgrades to websocket if the browser supports websocket.
Using SocketIO With Multiple Pods
When scaling up the server and having multiple pods, different clients may connect to different pods and few clients may not recieve the messages.
For example, consider a scenario where 3 clients connect to the server and join room Room A
. Due to load balancing, Client 1
and Client 2
connect to Server 1
but Client 3
connect to Server 2
. Client 1
emits a message to Server 1
, io.to("RoomA").emit()
, and the server broadcasts it to every client in the room socket.broadcast.to("Room A").emit()
. Here only Client 2
recieves this message and Client 3
does not as it is connected to Server 2
.

Solution: Using Redis Adapter
An Adapter is a server-side component which is responsible for broadcasting events to all or a subset of clients. SocketIO has default in memory adapter which keeps track of sockets that has joined to a particular room.
When scaling up the server, the problem of clients not receiving messages as it gets connected to different servers due to load balancing can be solved by replacing the default adapter with a Redis adapter and having a Redis instance running. The socketIO Redis adapter uses the Redis Pubsub to relay the events between different servers. Any events broadcasted by a server is published to the pubsub channel by the redis adapter. Each server subscribes to this Redis Pubsub channel and receives the events.

When socket 1
broadcasts messages to all sockets in Room A
, the Redis adapter in Server 1
pushes this message to the Redis Pubsub channel. Since Server 2
has subscribed to this channel, it receives the message and the Redis adpater in Server 2
sends this message to all sockets in Room A in Server 2
.
The redis adapter can be installed using the @socket.io/redis-adapter
library.
As per socketIO v4 and redis-adapter v7.
const { createClient } = require("redis");
const { createAdapter } = require("@socket.io/redis-adapter");
const pubClient = createClient({ host: "localhost", port: 6379 }); // creates a redis client for publishing the events
const subClient = pubClient.duplicate(); // creates a redis client for subscribing to events
io.adapter(createAdapter(pubClient, subClient)); // creates an adapter and attaches it to socketIO