Skip to content

Soham8763/Synapse

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

9 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Scalable Real-Time Chat Architecture

Redis Pub/Sub + Socket.io + MongoDB

🎯 Project Overview

A free, scalable real-time chat application designed for 10,000+ concurrent users using open-source technologies. This architecture supports both group chats and peer-to-peer messaging without any subscription costs.


πŸ—οΈ Architecture Components

Core Technologies (100% Free & Open Source)

  • Node.js + Express - Backend servers
  • Socket.io - Real-time WebSocket communication
  • Redis - Pub/Sub messaging & caching
  • MongoDB - Message persistence
  • RabbitMQ - Offline message queuing
  • Docker - Containerization for easy scaling
  • Nginx - Load balancing

πŸ“Š System Architecture Diagram

graph TB
    subgraph "Client Layer"
        U1[User 1<br/>Web/Mobile Client]
        U2[User 2<br/>Web/Mobile Client]
        U3[User 3<br/>Web/Mobile Client]
        U4[User 4<br/>Web/Mobile Client]
    end

    subgraph "Load Balancer"
        LB[Nginx Load Balancer]
    end

    subgraph "Application Layer"
        S1[Node.js Server 1<br/>Socket.io]
        S2[Node.js Server 2<br/>Socket.io]
        S3[Node.js Server N<br/>Socket.io]
    end

    subgraph "Message Broker"
        REDIS[Redis Pub/Sub<br/>Message Broadcasting]
    end

    subgraph "Data Layer"
        MONGO[(MongoDB<br/>Message Storage)]
        RABBIT[RabbitMQ<br/>Offline Messages]
    end

    U1 -.->|WebSocket| LB
    U2 -.->|WebSocket| LB
    U3 -.->|WebSocket| LB
    U4 -.->|WebSocket| LB

    LB --> S1
    LB --> S2
    LB --> S3

    S1 <--> REDIS
    S2 <--> REDIS
    S3 <--> REDIS

    S1 --> MONGO
    S2 --> MONGO
    S3 --> MONGO

    S1 <--> RABBIT
    S2 <--> RABBIT
    S3 <--> RABBIT
Loading

πŸ”„ Message Flow Workflow

Group Chat Message Flow

sequenceDiagram
    participant U1 as User 1
    participant S1 as Server 1
    participant R as Redis Pub/Sub
    participant M as MongoDB
    participant S2 as Server 2
    participant U2 as User 2
    participant RQ as RabbitMQ

    U1->>S1: 1. Send message "Hello everyone!"
    S1->>R: 2. Publish message to channel
    S1->>M: 3. Store message in database

    R->>S1: 4. Broadcast to Server 1
    R->>S2: 4. Broadcast to Server 2

    S1->>U1: 5. Echo back to sender (optional)

    alt User 2 is online
        S2->>U2: 6. Deliver message instantly
    else User 2 is offline
        S2->>RQ: 6. Queue message for later
        Note over RQ: Message queued until user comes online
        RQ->>U2: 7. Deliver when user reconnects
    end
Loading

Peer-to-Peer Message Flow

sequenceDiagram
    participant A as User A
    participant S1 as Server 1
    participant R as Redis
    participant M as MongoDB
    participant S2 as Server 2
    participant B as User B

    A->>S1: 1. Send personal message to User B
    Note over S1: Create P2P room: "p2p_userA_userB"

    S1->>R: 2. Publish to P2P channel
    S1->>M: 3. Store with recipientId

    R->>S2: 4. Broadcast to all servers
    Note over S2: Check if User B is connected here

    alt User B online on Server 2
        S2->>B: 5. Deliver personal message
    else User B offline
        S2->>RabbitMQ: 5. Queue for offline delivery
    end
Loading

πŸ’» Implementation Details

1. Server Setup (Node.js + Socket.io)

// server.js
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const redis = require('redis');
const mongoose = require('mongoose');

const app = express();
const server = http.createServer(app);
const io = socketIo(server);

// Redis clients
const redisClient = redis.createClient();
const redisSubscriber = redis.createClient();

// Message schema
const messageSchema = {
  roomId: String,
  senderId: String,
  recipientId: String, // Only for P2P messages
  content: String,
  messageType: String, // 'group' or 'personal'
  timestamp: Date,
  delivered: Boolean
};

// Socket connection handling
io.on('connection', (socket) => {
  console.log(`User connected: ${socket.id}`);

  // Join user to their personal room
  socket.on('join', (userId) => {
    socket.userId = userId;
    socket.join(`user_${userId}`);
  });

  // Handle group messages
  socket.on('group_message', async (data) => {
    const message = {
      roomId: data.roomId,
      senderId: socket.userId,
      content: data.content,
      messageType: 'group',
      timestamp: new Date()
    };

    // Publish to Redis
    redisClient.publish('chat_messages', JSON.stringify(message));

    // Store in MongoDB
    await saveMessage(message);
  });

  // Handle personal messages
  socket.on('personal_message', async (data) => {
    const roomId = createP2PRoom(socket.userId, data.recipientId);

    const message = {
      roomId,
      senderId: socket.userId,
      recipientId: data.recipientId,
      content: data.content,
      messageType: 'personal',
      timestamp: new Date()
    };

    // Publish to Redis
    redisClient.publish('chat_messages', JSON.stringify(message));

    // Store in MongoDB
    await saveMessage(message);
  });
});

// Redis subscription for receiving messages
redisSubscriber.subscribe('chat_messages');
redisSubscriber.on('message', (channel, data) => {
  const message = JSON.parse(data);

  if (message.messageType === 'group') {
    // Broadcast to room
    io.to(message.roomId).emit('new_message', message);
  } else {
    // Send to specific user
    io.to(`user_${message.recipientId}`).emit('personal_message', message);
  }
});

// Helper functions
const createP2PRoom = (userId1, userId2) => {
  return `p2p_${[userId1, userId2].sort().join('_')}`;
};

const saveMessage = async (message) => {
  // MongoDB save logic
  await Message.create(message);
};

2. Docker Configuration

# docker-compose.yml
version: '3.8'
services:
  # Multiple Node.js servers
  chat-server-1:
    build: .
    ports:
      - "3001:3000"
    environment:
      - SERVER_ID=server-1
      - REDIS_URL=redis://redis:6379
      - MONGO_URL=mongodb://mongo:27017/chatdb
    depends_on:
      - redis
      - mongo
      - rabbitmq

  chat-server-2:
    build: .
    ports:
      - "3002:3000"
    environment:
      - SERVER_ID=server-2
      - REDIS_URL=redis://redis:6379
      - MONGO_URL=mongodb://mongo:27017/chatdb
    depends_on:
      - redis
      - mongo
      - rabbitmq

  # Load balancer
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - chat-server-1
      - chat-server-2

  # Redis for pub/sub
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

  # MongoDB for persistence
  mongo:
    image: mongo:latest
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db

  # RabbitMQ for offline messages
  rabbitmq:
    image: rabbitmq:management-alpine
    ports:
      - "5672:5672"
      - "15672:15672"

volumes:
  mongo_data:

3. Database Management

// Database cleanup to prevent overflow
const cleanupOldMessages = async () => {
  const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);

  // Delete old messages
  await Message.deleteMany({
    timestamp: { $lt: thirtyDaysAgo }
  });

  console.log('Cleaned up messages older than 30 days');
};

// Run cleanup daily
setInterval(cleanupOldMessages, 24 * 60 * 60 * 1000);

// MongoDB indexes for performance
db.messages.createIndex({ "timestamp": 1 }, { expireAfterSeconds: 2592000 }); // TTL
db.messages.createIndex({ "roomId": 1, "timestamp": -1 });
db.messages.createIndex({ "senderId": 1, "timestamp": -1 });
db.messages.createIndex({ "recipientId": 1, "timestamp": -1 });

πŸš€ Scaling Strategy

Horizontal Scaling Plan

graph LR
    subgraph "1-100 Users"
        A[1 Server + Redis + MongoDB]
    end

    subgraph "100-1000 Users"
        B[2-3 Servers + Redis + MongoDB + Load Balancer]
    end

    subgraph "1000-5000 Users"
        C[5-8 Servers + Redis Cluster + MongoDB Replica Set]
    end

    subgraph "5000-10000+ Users"
        D[10+ Servers + Redis Cluster + MongoDB Sharding + CDN]
    end
Loading

Resource Allocation (Free Tier Friendly)

Users Servers RAM per Server CPU per Server Total Cost
0-100 1 512MB 1 vCPU FREE
100-1K 2-3 1GB 1 vCPU FREE
1K-5K 5-8 2GB 2 vCPU FREE
5K-10K+ 10+ 4GB 2 vCPU $0-50/month

Using free tiers from platforms like Railway, Render, Heroku, DigitalOcean credits, etc.


πŸ”§ Free Hosting Options

Deployment Platforms (Free Tiers)

  1. Railway - Free tier with 512MB RAM
  2. Render - Free tier with 512MB RAM
  3. Fly.io - Free allowances
  4. Heroku - Limited free tier
  5. DigitalOcean - $200 student credits
  6. Google Cloud - $300 free credits
  7. AWS - Free tier for 12 months

Database Hosting (Free)

  • MongoDB Atlas - 512MB free cluster
  • Redis Cloud - 30MB free tier
  • ElephantSQL - 20MB PostgreSQL free
  • PlanetScale - MySQL free tier

πŸ“ˆ Performance Optimizations

1. Connection Pooling

// MongoDB connection pooling
mongoose.connect(mongoUrl, {
  maxPoolSize: 10,
  serverSelectionTimeoutMS: 5000,
  socketTimeoutMS: 45000,
});

// Redis connection pooling
const redisPool = new Redis.Cluster([
  { host: 'redis-1', port: 6379 },
  { host: 'redis-2', port: 6379 },
]);

2. Message Batching

// Batch messages for better performance
const messageBatch = [];
const BATCH_SIZE = 100;
const BATCH_TIMEOUT = 1000; // 1 second

const processBatch = async () => {
  if (messageBatch.length > 0) {
    await Message.insertMany(messageBatch);
    messageBatch.length = 0;
  }
};

// Process batch every second or when full
setInterval(processBatch, BATCH_TIMEOUT);

3. Caching Strategy

// Cache frequent data in Redis
const cacheUser = async (userId, userData) => {
  await redisClient.setex(`user:${userId}`, 3600, JSON.stringify(userData));
};

const getCachedUser = async (userId) => {
  const cached = await redisClient.get(`user:${userId}`);
  return cached ? JSON.parse(cached) : null;
};

πŸ” Security Considerations

1. Rate Limiting

const rateLimit = require('express-rate-limit');

const messageLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 30, // 30 messages per minute per IP
  message: 'Too many messages, please slow down'
});

app.use('/api/messages', messageLimiter);

2. Input Validation

const joi = require('joi');

const messageSchema = joi.object({
  content: joi.string().max(1000).required(),
  roomId: joi.string().required(),
  recipientId: joi.string().when('messageType', {
    is: 'personal',
    then: joi.required()
  })
});

🎯 Key Features Supported

βœ… Real-time messaging - Instant message delivery βœ… Group chats - Multiple users in rooms βœ… Private messages - Peer-to-peer communication βœ… Offline message queuing - Messages delivered when user returns βœ… Message persistence - Chat history stored βœ… Horizontal scaling - Add more servers as needed βœ… Cross-server communication - Users on different servers can chat βœ… Auto cleanup - Prevents database overflow βœ… Load balancing - Distribute users across servers βœ… Zero subscription costs - 100% open source stack


πŸ“ Getting Started

Quick Setup

# Clone and setup
git clone <your-repo>
cd scalable-chat-app

# Install dependencies
npm install

# Start with Docker
docker-compose up -d

# Or start individual services
npm run start:server1
npm run start:server2
npm run start:nginx

Environment Variables

# .env
REDIS_URL=redis://localhost:6379
MONGO_URL=mongodb://localhost:27017/chatdb
RABBITMQ_URL=amqp://localhost:5672
SERVER_PORT=3000
JWT_SECRET=your-secret-key

πŸŽ“ Perfect for Students!

This architecture is designed specifically for student projects and learning purposes:

  • No subscription costs - Everything is open source
  • Free hosting options - Use free tiers and student credits
  • Scalable design - Can grow from 10 to 10,000+ users
  • Industry-standard - Uses real-world technologies
  • Well-documented - Easy to understand and modify
  • Portfolio-ready - Impressive for job applications

Total monthly cost: $0-50 even for 10,000+ users!


This documentation provides a complete blueprint for building a scalable, production-ready chat application without breaking the bank. Perfect for students who want to build something impressive while learning industry-standard technologies.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages