🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

So erstellen Sie im Jahr 2026 eine Echtzeit-App mit WebSockets und Node.js

⏱️6 min read  ·  1,129 words

Echtzeitfunktionen – Live-Chat, Benachrichtigungen, gemeinsame Bearbeitung, Live-Dashboards – erfordern eine dauerhafte Verbindung zwischen Client und Server. Im Jahr 2026WebSockets über Socket.io bleibt für die meisten Echtzeitanwendungen die praktischste und am weitesten verbreitete Lösung. Dieses Tutorial baut ein vollständiges Echtzeitsystem mit Skalierung über Redis Pub/Sub auf.

WebSockets vs. SSE vs. Long Polling

Methode Bidirektional Latenz Am besten für
WebSockets Ja ~1ms Chat, Spiele, Tools für die Zusammenarbeit
Vom Server gesendete Ereignisse Nein (nur Server → Client) ~5ms Live-Dashboards, Benachrichtigungen
Lange Abfrage Simuliert 100-500 ms Fallback für eingeschränkte Netzwerke

Projekt-Setup

mkdir realtime-app && cd realtime-app
npm init -y
npm install express socket.io ioredis cors

# Frontend dependencies
npm install -D vite

Server: Express + Socket.io

// server.js
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const Redis = require('ioredis');
const cors = require('cors');

const app    = express();
const server = createServer(app);
const io     = new Server(server, {
  cors: {
    origin:  "http://localhost:5173",   // Vite dev server
    methods: ["GET", "POST"]
  }
});

app.use(cors());
app.use(express.json());

// Redis Pub/Sub for multi-instance scaling
const pub = new Redis({ host: 'localhost', port: 6379 });
const sub = new Redis({ host: 'localhost', port: 6379 });

// ── Active users tracking ─────────────────────────────────
const activeUsers = new Map();  // socketId → { userId, username, room }

// ── Subscribe to Redis (messages from other server instances) ──
sub.subscribe('chat', 'notifications', (err, count) => {
  console.log(`Subscribed to ${count} Redis channels`);
});

sub.on('message', (channel, message) => {
  const data = JSON.parse(message);
  if (channel === 'chat') {
    io.to(data.room).emit('message', data);
  }
  if (channel === 'notifications') {
    io.to(data.userId).emit('notification', data);
  }
});

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

  // ── Authenticate ───────────────────────────────────────
  socket.on('authenticate', ({ userId, username }) => {
    activeUsers.set(socket.id, { userId, username });
    socket.join(userId);   // join personal room for direct notifications
    socket.emit('authenticated', { userId, socketId: socket.id });
    console.log(`User authenticated: ${username} (${userId})`);
  });

  // ── Join chat room ─────────────────────────────────────
  socket.on('join_room', ({ roomId }) => {
    const user = activeUsers.get(socket.id);
    if (!user) return socket.emit('error', 'Not authenticated');

    socket.join(roomId);
    activeUsers.set(socket.id, { ...user, room: roomId });

    // Notify others in room
    socket.to(roomId).emit('user_joined', {
      userId:   user.userId,
      username: user.username,
      roomId,
      timestamp: Date.now()
    });

    console.log(`${user.username} joined room: ${roomId}`);
  });

  // ── Send message ───────────────────────────────────────
  socket.on('send_message', async ({ roomId, content }) => {
    const user = activeUsers.get(socket.id);
    if (!user) return socket.emit('error', 'Not authenticated');

    const message = {
      id:        `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,
      userId:    user.userId,
      username:  user.username,
      content,
      room:      roomId,
      timestamp: Date.now()
    };

    // Publish to Redis so ALL server instances receive it
    pub.publish('chat', JSON.stringify(message));

    // TODO: persist to database here
    // await db.messages.create({ data: message });
  });

  // ── Typing indicator ───────────────────────────────────
  socket.on('typing', ({ roomId, isTyping }) => {
    const user = activeUsers.get(socket.id);
    if (!user) return;
    socket.to(roomId).emit('user_typing', {
      userId:   user.userId,
      username: user.username,
      isTyping
    });
  });

  // ── Disconnect ─────────────────────────────────────────
  socket.on('disconnect', () => {
    const user = activeUsers.get(socket.id);
    if (user?.room) {
      socket.to(user.room).emit('user_left', {
        userId:   user.userId,
        username: user.username
      });
    }
    activeUsers.delete(socket.id);
    console.log(`Client disconnected: ${socket.id}`);
  });
});

// ── REST API: send notification from backend ──────────────
app.post('/notify', (req, res) => {
  const { userId, type, message } = req.body;
  pub.publish('notifications', JSON.stringify({ userId, type, message, timestamp: Date.now() }));
  res.json({ ok: true });
});

server.listen(3001, () => console.log('Server running on port 3001'));

Client: React + Socket.io

// hooks/useSocket.ts
import { useEffect, useRef, useState } from 'react';
import { io, Socket } from 'socket.io-client';

export function useSocket(userId: string, username: string) {
  const socket  = useRef<Socket | null>(null);
  const [connected, setConnected] = useState(false);
  const [messages, setMessages]   = useState<Message[]>([]);
  const [notifications, setNotifications] = useState<Notification[]>([]);
  const [typingUsers, setTypingUsers]     = useState<Set<string>>(new Set());

  useEffect(() => {
    socket.current = io('http://localhost:3001');

    socket.current.on('connect', () => {
      setConnected(true);
      socket.current!.emit('authenticate', { userId, username });
    });

    socket.current.on('message', (msg: Message) => {
      setMessages(prev => [...prev, msg]);
    });

    socket.current.on('notification', (notif: Notification) => {
      setNotifications(prev => [notif, ...prev]);
    });

    socket.current.on('user_typing', ({ username: typer, isTyping }) => {
      setTypingUsers(prev => {
        const next = new Set(prev);
        isTyping ? next.add(typer) : next.delete(typer);
        return next;
      });
    });

    socket.current.on('disconnect', () => setConnected(false));

    return () => { socket.current?.disconnect(); };
  }, [userId, username]);

  const joinRoom  = (roomId: string) => socket.current?.emit('join_room', { roomId });
  const sendMessage = (roomId: string, content: string) =>
    socket.current?.emit('send_message', { roomId, content });
  const setTyping = (roomId: string, isTyping: boolean) =>
    socket.current?.emit('typing', { roomId, isTyping });

  return { connected, messages, notifications, typingUsers, joinRoom, sendMessage, setTyping };
}

Skalierung mit Redis Pub/Sub

Das Redis Pub/Sub-Muster im Servercode ermöglicht eine horizontale Skalierung: Wenn eine Nachricht auf einer Node.js-Instanz eintrifft, wird sie auf Redis veröffentlicht. Alle anderen Instanzen (bei Redis abonniert) empfangen es und senden es an ihre verbundenen Clients. Das bedeutet, dass Sie 10 Node.js-Instanzen hinter einem Load Balancer ausführen können und Nachrichten alle Benutzer erreichen, unabhängig davon, mit welcher Instanz sie verbunden sind.

# Run multiple instances
PORT=3001 node server.js &
PORT=3002 node server.js &
PORT=3003 node server.js &

# Nginx load balancer with sticky sessions (WebSocket requirement)
upstream websocket {
    ip_hash;  # sticky sessions - same client hits same server
    server localhost:3001;
    server localhost:3002;
    server localhost:3003;
}

Häufig gestellte Fragen

F: Socket.io vs. native WebSocket-API?
A: Socket.io fügt hinzu: automatische Wiederverbindung, Räume/Namespaces, Fallback auf Long-Polling und ereignisbasierte API. Native WebSocket ist eine niedrigere Ebene. Verwenden Sie Socket.io für die Produktion – allein die Wiederverbindungslogik spart Stunden Arbeit.

F: Wie viele gleichzeitige WebSocket-Verbindungen kann Node.js verarbeiten?
A: Auf einem einzelnen 4-Core-Server mit 8 GB RAM können Sie je nach Nachrichtenhäufigkeit mit 10.000 bis 50.000 gleichzeitigen Verbindungen rechnen. Socket.io mit Redis-Clustering skaliert auf Millionen über mehrere Server hinweg.

F: Funktionieren WebSockets über Firewalls und Proxys hinweg?
A: Die meisten modernen Netzwerke erlauben WebSockets auf Port 443 (HTTPS/WSS). Socket.io greift auf Long-Polling zurück, wenn WebSocket blockiert ist. Verwenden Sie WSS (sicheres WebSocket) in der Produktion.

F: Wie kann ich Chatnachrichten dauerhaft speichern?
A: In PostgreSQL oder MongoDB speichern. Index nach (roomId, Zeitstempel) für schnelles Abrufen. Laden Sie die letzten 50 Nachrichten, wenn ein Benutzer einem Raum über die REST-API beitritt; Streamen Sie neue Nachrichten über WebSocket.

F: Gibt es eine verwaltete WebSocket-Dienstalternative?
A: Pusher, Ably und AWS API Gateway WebSocket sind verwaltete Alternativen. Höhere Kosten, aber kein Infrastrukturmanagement. Gut für kleine Mengen oder wenn die Entwicklerzeit ein Engpass ist.

Fazit

WebSockets mit Socket.io und Redis Pub/Sub ist die bewährte, skalierbare Architektur für Echtzeitanwendungen im Jahr 2026. Der Servercode in diesem Tutorial verwaltet Tausende gleichzeitiger Benutzer auf einer einzigen Instanz und skaliert bei steigendem Bedarf horizontal über Redis. Beginnen Sie mit einer einzelnen Instanz, fügen Sie Redis hinzu, wenn Sie über einen Server hinaus skalieren müssen, und wechseln Sie nur dann zu verwalteten WebSocket-Diensten, wenn der Betriebsaufwand zum Problem wird.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা