socketio / socketio/socket.io

Scaling a Node.js Application with Socket.IO in Kubernetes

Aperta
#5,167 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

question
Lingua principale
TypeScript
Stelle
63.2k
Fork
10.3k
Merge medio
11g 20h
PR unite (30g)
2

Descrizione

Introduction

  • When deploying a Node.js application that uses Socket.IO for real-time communication, scaling can be challenging due to the need for session consistency and message broadcasting across multiple instances. Using a Redis adapter and Redis emitter can help manage these challenges effectively.

Prerequisites

  • Kubernetes Cluster: A running Kubernetes cluster.
  • Redis: A Redis instance accessible by the Node.js application.
  • Node.js Application: An existing Node.js application using Socket.IO.

Screenshot_796
Screenshot_797

Node Code:

import express from 'express';
import http from 'http';
import { Server } from 'socket.io';
import cors from 'cors';
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';
import { Emitter } from '@socket.io/redis-emitter';

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

// Get Redis external IP and port from environment variables
const redisHost = process.env.REDIS_HOST || '192.168.0.49'; // Update this to your Redis IP
const redisPort = Number(process.env.REDIS_PORT) || 31225; // Update this to your Redis port

// Create a Redis client
const redisClient = createClient({ url: `redis://${redisHost}:${redisPort}` });
const subClient = redisClient.duplicate();


// Handle Redis connection errors
redisClient.on('error', (err) => {
  console.error('Redis client error:', err);
});

// Set up CORS options for Socket.IO
const io = new Server(server, {
  cors: {
    origin: '*',
    methods: ['GET', 'POST'],
    credentials: true,
  },
});

// Connect to Redis and configure Socket.IO
redisClient.connect().then(() => {
  console.log('Connected to Redis');

  // Set the Redis adapter for Socket.IO
  io.adapter(createAdapter(redisClient, subClient));

  // Create a Redis emitter
  const emitter = new Emitter(redisClient);

  // Emit a message every 5 seconds
  setInterval(() => {
    const currentTime = new Date();
    console.log('Emitting time:', currentTime);
    emitter.emit('time', currentTime.toISOString()); // Emit the current time in ISO format
  }, 5000); // Emit every 5 seconds
}).catch((err) => {
  console.error('Redis connection error:', err);
});

// Enable CORS for all routes
app.use(cors());

// Handle Socket.IO connections
io.on('connection', (socket) => {
  console.log('New client connected');

  // Listen for the 'time' event emitted by Redis
  socket.on('time', (time) => {
    console.log('Time received:', time);
    socket.emit('time', time); // Send the time to the connected client
  });

  // Listen for the 'time' event from the emitter and send to the client
  socket.on('subscribeToTime', () => {
    console.log('Client subscribed to time updates');
    socket.join('time-updates');
  });

  // Listen for incoming messages and broadcast them
  socket.on('sendMessage', (message) => {
    io.emit('receiveMessage', message);
  });

  socket.on('disconnect', () => {
    console.log('Client disconnected');
  });
});

// Listen to Redis for 'time' events and broadcast them to all clients
subClient.on('message', (channel: string, message: string) => {
  if (channel === 'time') {
    const time = new Date(message);
    io.to('time-updates').emit('time', time); // Emit the time to subscribed clients
  }
});

// Subscribe to the 'time' channel
subClient.subscribe('time', (err) => {
  if (err) {
    console.error('Failed to subscribe to time channel:', err);
  } else {
    console.log('Subscribed to time channel');
  }
});

// Start the server
const PORT = process.env.PORT || 5001;
server.listen(PORT, () => console.log(`Server running on port ${PORT}`));

App.tsx

Client App code (react js)
import React from 'react';
import { Chat } from './Chat';
import { io } from 'socket.io-client';

// Use an environment variable or fallback to a default URL
console.log(process.env.REACT_APP_SOCKET_URL)
const socket = io(process.env.REACT_APP_SOCKET_URL || 'http://localhost:5001');
console.log(socket)
const App: React.FC = () => {
    return (
        <div>
            <h1>Group Chat Application</h1>
            <Chat socket={socket} />
        </div>
    );
};

export default App;

Chat.tsx

import React, { useEffect, useState } from 'react';
import { Socket } from 'socket.io-client'; // Import the Socket type

interface ChatProps {
    socket: Socket; // Use the Socket type here
}

export const Chat: React.FC<ChatProps> = ({ socket }) => {
    const [message, setMessage] = useState('');
    const [messages, setMessages] = useState<string[]>([]);

    useEffect(() => {
        socket.on('receiveMessage', (message: string) => {
            setMessages((prev) => [...prev, message]);
        });

        return () => {
            socket.off('receiveMessage'); // Clean up the listener on unmount
        };
    }, [socket]);

    const sendMessage = () => {
        if (message) {
            socket.emit('sendMessage', message);
            setMessage('');
        }
    };

    return (
        <div>
            
            <div>
                {messages.map((msg, index) => (
                    <div key={index}>{msg}</div>
                ))}
            </div>
            <input
                value={message}
                onChange={(e) => setMessage(e.target.value)}
                onKeyDown={(e) => {
                    if (e.key === 'Enter') {
                        sendMessage();
                    }
                }}
            />
            <button onClick={sendMessage}>Send</button>


        </div>
    );
};

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

La issue fornisce snippet dell’applicazione per una configurazione con Node.js, React, Redis e Kubernetes, ma non indica alcun file del repository, test o modifica concreta richiesta. Inizia chiarendo se è destinata a essere documentazione e dove dovrebbe essere collocata; non sono specificati i criteri di completamento.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
kubernetes, nodejs, react, redis, typescript
Ambito
backend, distributed-systems, frontend, infrastructure
Tipo di issue
Documentazione
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Ferma
Chiarezza
Da chiarire
Idoneità per principianti
15/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.