Support Online
Skip to main content

Pub/Sub Architecture with Node.js and Redis: Scalable Chat Application

What will you learn in this guide?

In this guide, you will learn the Publish/Subscribe (Pub/Sub) architecture, one of the basic building blocks of real-time applications.
You will develop a chat application that works with Node.js and WebSocket, then make the application suitable for a multi-server environment by adding Redis as a message broker.

Technical Summary

This guide explains how to scale a Node.js-based WebSocket application.
The problem is that chat applications running on a single server cannot communicate with different servers.
The solution is to share messages between servers with the Redis Pub/Sub mechanism.


Preliminary Preparations

Before you start, you need to have the following requirements ready:

  • Node.js (v12 and above)
  • VS Code or similar code editor
  • Redis (local or remote server)
  • Basic knowledge of JavaScript, HTML and WebSocket

1. Server Side Installation

1.1 Starting the Project

npm init -y
npm install ws ioredis
  • These commands create the Node.js project and install WebSocket and Redis dependencies.

1.2 Creating a Simple HTTP Server


const http = require("http");

const server = http.createServer((req, res) => {
res.end("Merhaba Chat Uygulaması");
});

const PORT = process.argv[2] || 3459;
server.listen(PORT, () => {
console.log(`Sunucu ${PORT} portunda çalışıyor`);
});
  • This code starts a basic web server with Node.js' built-in HTTP module.

2. Preparing the HTML Interface

2.1 index.html File


<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8" />
<title>Pub/Sub Chat</title>
</head>
<body>
<h2>Pub/Sub Chat Uygulaması</h2>

<div id="mesajKutusu"
style="border:1px solid #ddd;height:300px;overflow-y:auto;padding:10px;">
</div>

<form id="mesajFormu" style="margin-top:10px;">
<input type="text" id="mesajMetni" placeholder="Mesajınızı yazın..." />
<button type="submit">Gönder</button>
</form>

<script>
const socket = new WebSocket(`ws://${location.host}`);
const mesajKutusu = document.getElementById("mesajKutusu");

socket.onmessage = (event) => {
if (event.data instanceof Blob) {
event.data.text().then(yaz);
} else {
yaz(event.data);
}
};

function yaz(text) {
const p = document.createElement("p");
p.textContent = text;
mesajKutusu.appendChild(p);
}

document.getElementById("mesajFormu").addEventListener("submit", (e) => {
e.preventDefault();
const input = document.getElementById("mesajMetni");
socket.send(input.value);
input.value = "";
});
</script>
</body>
</html>
  • This file establishes a WebSocket connection and displays messages in the user interface.

2.2 Publish HTML File from Server


const fs = require("fs");
const path = require("path");

const server = http.createServer((req, res) => {
const filePath = path.join(__dirname, "index.html");
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(500);
return res.end("Dosya okunamadı");
}
res.writeHead(200, { "Content-Type": "text/html" });
res.end(data);
});
});
  • This code returns HTML interface to requests from the browser.

3. WebSocket Integration


const WebSocket = require("ws");
const wss = new WebSocket.Server(&#123; server &#125;);

wss.on("connection", (client) => {
client.on("message", (mesaj) => {
wss.clients.forEach((c) => {
if (c.readyState === WebSocket.OPEN) {
c.send(mesaj);
}
});
});
});
  • This structure distributes incoming messages to all clients connected to the same server.

4. Scaling Problem

  1. Messaging is seamless when a single server is running.
  2. But when the application grows, more than one server is required.
  • At this point, the different servers are not aware of each other.

5. Pub/Sub Architecture with Redis

  1. Redis acts as a message broker here.
  • A server publishes the message to Redis, other servers subscribe to this message.

5.1 Redis Integration


const Redis = require("ioredis");

const redisPublisher = new Redis();
const redisSubscriber = new Redis();

redisSubscriber.subscribe("genel_sohbet");

wss.on("connection", (client) => {
client.on("message", (mesaj) => {
redisPublisher.publish("genel_sohbet", mesaj);
});
});

redisSubscriber.on("message", (kanal, mesaj) => {
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(mesaj);
}
});
});
  • This code allows messages to be shared between all servers.

5.2 Multi-Server Testing


node app.js 3459
node app.js 3460
  • Servers running on different ports share messages via Redis.

Frequently Asked Questions

  1. When is Pub/Sub architecture preferred? It is used in real-time and loosely coupled systems.

  2. Can there be a database instead of Redis? Technically it is possible, but performance suffers.

  3. How many users does this structure support? It depends on Redis and server capacity.

  4. Is there a single point error? Redis is available if it works alone.

  5. Are there alternative message brokers? RabbitMQ and Kafka are common alternatives.


Result

In this guide, we made a simple chat application with Node.js scalable with the GenixNode architecture. Thanks to Redis, we provided real-time messaging between different servers. This approach forms the basis of modern, high-traffic applications.