WebSocket in Go: Erstellung von Echtzeit-Anwendungen auf fortgeschrittenem Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einführung: Über einfaches WebSocket in Go hinaus



Grundlegende WebSocket-Beispiele in Go zeigen normalerweise eine einfache Echo-Verbindung. In echten Produktionsprojekten — von Trading-Terminals über Multiplayer-Spiele bis hin zu Benachrichtigungssystemen — wird viel mehr benötigt: Raumverwaltung, Graceful Shutdown, Skalierung und ordnungsgemäße Fehlerbehandlung. In diesem Artikel analysieren wir ein fortgeschrittenes Muster für die Erstellung von Echtzeitanwendungen in Go unter Verwendung der gorilla/websocket-Bibliothek.



Wir erstellen eine Architektur basierend auf Hub (zentraler Verteiler) und Client (einzelne Verbindung), fügen Raumunterstützung, einen Fehlertoleranzmechanismus hinzu und zeigen, wie man den Server ordnungsgemäß herunterfährt.



Hub- und Client-Architektur: Die Grundlage eines Echtzeit-Servers



Warum Hub?

Jeder WebSocket-Client ist eine separate Goroutine. Sie direkt von einem HTTP-Handler aus zu verwalten, ist gefährlich: Es führt zu Datenkonflikten und Speicherlecks. Das Hub-Muster löst dieses Problem, indem es als einzelner Kanal für Registrierung, Entfernung und Nachrichtenverteilung fungiert.



Hub-Implementierung

package main

import ( "log" "sync")

type Client struct { hub *Hub send chan []byte room string}

type Hub struct { clients map[*Client]bool broadcast chan Message register chan *Client unregister chan *Client mu sync.RWMutex}

type Message struct { Room string Data []byte Sender *Client}

func NewHub() *Hub { return &Hub{ clients: make(map[*Client]bool), broadcast: make(chan Message, 256), register: make(chan *Client), unregister: make(chan *Client), }}

func (h *Hub) Run() { for { select { case client := <-h.register: h.mu.Lock() h.clients[client] = true h.mu.Unlock() log.Printf("Client verbunden. Gesamt: %d", len(h.clients))

case client := <-h.unregister: h.mu.Lock() if _, ok := h.clients[client]; ok { delete(h.clients, client) close(client.send) } h.mu.Unlock()

case message := <-h.broadcast: h.mu.RLock() for client := range h.clients { // Nur an den Zielraum senden if client.room == message.Room || message.Room == "" { select { case client.send <- message.Data: default: // Wenn der Kanal voll ist — Client trennen h.mu.RUnlock() h.unregister <- client h.mu.RLock() } } } h.mu.RUnlock() } }}


Schlüsselpunkte:

  • Kanäle — register, unregister, broadcast — synchronisieren Goroutinen sicher.
  • RWMutex — erlaubt gleichzeitiges Lesen der Client-Liste, blockiert aber Schreibvorgänge.
  • Raumbasierte Weiterleitung — eine Nachricht wird nur an Clients im selben Raum gesendet.


Raum- und Abonnementverwaltung



Räume sind logische Gruppen von Clients. Zum Beispiel hat in einem Chat jede Unterhaltung ihren eigenen Raum. In unserem Hub wird der Raum durch das room-Feld in Client festgelegt. Sehen wir uns an, wie ein Client einen Raum abonniert.



HTTP-Handler mit WebSocket-Upgrade

import (    "net/http"    "github.com/gorilla/websocket")

var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true // Für Produktion durch Ursprungsprüfung ersetzen },}

func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err !=

Blogs

Buchempfehlungen