Einleitung: Das Dilemma des Entwicklers
\n\nStellen Sie sich vor: Sie schreiben einen Chat, einen Börsenterminal oder ein Online-Spiel in Go. Der Client muss Aktualisierungen sofort erhalten. Der gute alte AJAX mit periodischem Abfragen (Polling) fällt weg – zu viele nutzlose Anfragen, die sowohl den Server als auch den Akku des Benutzers belasten. Es bleiben zwei Kandidaten: Long Polling (langes Abfragen) und WebSocket.
\n\nBeide lösen die Aufgabe der „Echtzeit“, tun dies jedoch auf grundlegend unterschiedliche Weise. Welche sollten Sie für Ihre Go-Anwendung wählen? Lassen Sie es uns ohne Umschweife klären, mit lebendigen Codebeispielen und einer ehrlichen Analyse der Vor- und Nachteile. Dieser Artikel ist Ihr Navigator in der Welt der Echtzeit-Kommunikation in Go.
\n\nWas ist Long Polling in Go?
\n\nLong Polling ist eine Weiterentwicklung des normalen Polling. Der Client sendet eine HTTP-Anfrage an den Server, aber der Server antwortet nicht sofort, wenn keine Daten vorliegen. Er „hängt“ (hält die Verbindung offen) und wartet, bis ein neues Ereignis eintritt. Sobald das Ereignis eintritt, sendet der Server eine Antwort und der Client erstellt sofort eine neue Long Polling-Anfrage. Der Zyklus wiederholt sich.
\n\nIn Go wird dies dank Goroutinen und Kanälen sehr elegant implementiert. Der Server kann den Anfragen-Handler „schlafen legen“, bis Daten verfügbar sind.
\n\nBeispiel eines Long Polling-Servers in Go
\n\npackage main\n\nimport (\n "fmt"\n "net/http"\n "time"\n)\n\n// Kanal, in den neue Nachrichten eingehen\nvar messages = make(chan string, 10)\n\nfunc longPollHandler(w http.ResponseWriter, r *http.Request) {\n select {\n case msg := <-messages:\n fmt.Fprintf(w, "Neue Nachricht: %s", msg)\n case <-time.After(30 * time.Second):\n // Timeout – wenn 30 Sekunden lang keine Nachrichten eingehen\n http.Error(w, "Timeout", http.StatusGatewayTimeout)\n }\n}\n\nfunc main() {\n // Goroutine, die das Eintreffen neuer Nachrichten simuliert\n go func() {\n for {\n time.Sleep(5 * time.Second)\n messages <- fmt.Sprintf("Zeit: %s", time.Now().String())\n }\n }()\n\n http.HandleFunc("/poll", longPollHandler)\n http.ListenAndServe(":8080", nil)\n}\n\n\nDer Client in JavaScript sieht so aus: Nach Erhalt der Antwort (oder eines Timeouts) ruft er sofort erneut /poll auf. Einfach, zuverlässig, funktioniert über alle Proxys und Firewalls hinweg.
WebSocket in Go: Vollduplex
\n\nWebSocket ist ein Protokoll, das nach dem Handshake (HTTP Upgrade) die Verbindung in einen permanenten bidirektionalen Kanal umwandelt. Der Server kann dem Client jederzeit Daten senden, und der Client dem Server. Keine wiederholten Anfragen, keine Timeouts.
\n\nIn Go hat die Standardbibliothek keine integrierte Unterstützung für WebSocket, aber es gibt den Goldstandard – das Paket gorilla/websocket. Es ist stabil, gut dokumentiert und wird in tausenden Projekten im Produktionseinsatz verwendet.
Beispiel eines WebSocket-Servers in Go
\n\npackage main\n\nimport (\n "log"\n "net/http"\n "time"\n\n "github.com/gorilla/websocket"\n)\n\nvar upgrader = websocket.Upgrader{\n CheckOrigin: func(r *http.Request) bool {\n return true // Für die Entwicklung. In der Produktion prüfen!\n },\n}\n\nfunc wsHandler(w http.ResponseWriter, r *http.Request) {\n conn, err := upgrader.Upgrade(w, r, nil)\n if err != nil {\n log.Print("Upgrade fehlgeschlagen:", err)\n return\n }\n defer conn.Close()\n\n // Endlosschleife zum Lesen und Schreiben\n for {\n // Nachricht vom Client lesen\n _, message, err := conn.ReadMessage()\n if err != nil {\n log.Println("Lesefehler:", err)\n break\n }\n log.Printf("Empfangen: %s", message)\n\n // Antwort senden (oder Push-Benachrichtigung)\n err = conn.WriteMessage(websocket.TextMessage, []byte("Server: "+time.Now().String()))\n if err != nil {\n log.Println("Schreibfehler:", err)\n break\n }\n }\n}\n\nfunc main() {\n http.HandleFunc("/ws", wsHandler)\n log.Pr