WebSocket auf TypeScript: Fortgeschrittene Echtzeit-Anwendungen erstellen
\n\nEchtzeit-Anwendungen sind zu einem wesentlichen Bestandteil des modernen Webs geworden. Von Chats und Benachrichtigungen bis hin zu Online-Spielen und Handelsplattformen – die WebSocket-Technologie sorgt für eine permanente bidirektionale Verbindung zwischen Client und Server. Auf fortgeschrittenem Niveau erfordert die Entwicklung solcher Systeme nicht nur ein Verständnis des Protokolls, sondern auch die Fähigkeit, ausfallsichere, skalierbare Architekturen zu erstellen. In diesem Artikel werden wir uns ansehen, wie man Production-Grade-WebSocket-Anwendungen auf TypeScript unter Verwendung bewährter Methoden und Muster erstellt.
\n\n1. Architektur und Clusterbildung eines WebSocket-Servers
\n\nEin einzelner WebSocket-Server wird der Last eines realen Projekts nicht standhalten. Die Hauptaufgabe besteht darin, die Clusterbildung und den Nachrichtenaustausch zwischen den Instanzen zu organisieren. Betrachten wir eine Architektur mit der ws-Bibliothek und einem Redis-Ereignisbus.
Abhängigkeiten installieren:
\nnpm install ws redis ioredis @types/ws @types/ioredis\n\nEinen grundlegenden geclusterten Server erstellen:
\n\nimport { WebSocketServer, WebSocket } from 'ws';\nimport Redis from 'ioredis';\n\nconst redisPub = new Redis();\nconst redisSub = new Redis();\n\nconst wss = new WebSocketServer({ port: 8080 });\n\n// Kanal für die serverübergreifende Kommunikation\nconst CHANNEL = 'ws:messages';\n\nredisSub.subscribe(CHANNEL, (err, count) => {\n if (err) console.error('Abonnementfehler:', err);\n console.log(`Auf ${count} Kanäle abonniert`);\n});\n\n// Empfangen von Nachrichten von anderen Instanzen\nredisSub.on('message', (channel, message) => {\n const parsed = JSON.parse(message);\n wss.clients.forEach((client) => {\n if (client.readyState === WebSocket.OPEN) {\n client.send(parsed.data);\n }\n });\n});\n\nwss.on('connection', (ws) => {\n console.log('Neue Verbindung');\n\n ws.on('message', (data) => {\n const message = data.toString();\n // In Redis für alle Instanzen veröffentlichen\n redisPub.publish(CHANNEL, JSON.stringify({ data: message }));\n });\n});\n\nVorteile:
\n- \n
- Horizontale Skalierung – fügen Sie beliebig viele Server hinzu \n
- Ausfallsicherheit – fällt eine Instanz aus, arbeiten die anderen weiter \n
- Einheitlicher Nachrichtenbus über Redis Pub/Sub \n
2. Authentifizierung und Sitzungsverwaltung
\n\nIn Production-Anwendungen darf man nicht authentifizierten Verbindungen nicht vertrauen. Implementieren wir die Token-Überprüfung mittels JWT beim Verbindungsaufbau.
\n\njsonwebtoken installieren:
npm install jsonwebtoken @types/jsonwebtoken\n\nMiddleware für die WebSocket-Authentifizierung erstellen:
\n\nimport { IncomingMessage } from 'http';\nimport jwt from 'jsonwebtoken';\n\nconst JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';\n\ninterface AuthenticatedSocket extends WebSocket {\n userId?: string;\n}\n\nfunction authenticate(ws: AuthenticatedSocket, req: IncomingMessage): boolean {\n const url = new URL(req.url || '', 'http://localhost');\n const token = url.searchParams.get('token');\n\n if (!token) {\n ws.close(4001, 'Token nicht bereitgestellt');\n return false;\n }\n\n try {\n const decoded = jwt.verify(token, JWT_SECRET) as { userId: string };\n ws.userId = decoded.userId;\n console.log(`Benutzer ${decoded.userId} authentifiziert`);\n return true;\n } catch (err) {\n ws.close(4002, 'Ungültiges Token');\n return false;\n }\n}\n\n// Verwendung\nwss.on('connection', (ws: AuthenticatedSocket, req) => {\n if (!authenticate(ws, req)) return;\n\n ws.on('message', (data) => {\n // Jetzt kennen wir die userId\n console.log(`Nachricht von ${ws.userId}: ${data}`);\n });\n});\n\nEmpfehlungen:
\n- \n
- Übergeben Sie das Token beim Verbindungsaufbau als Query-Parameter oder in Headern \n
- Verwenden Sie kurzlebige Access-Tokens und Refresh-Tokens \n
- Speichern Sie Sitzungen in Redi