Introduction: The Developer's Dilemma
\n\nImagine: you are writing a chat, a stock exchange terminal, or an online game in Go. The client needs to receive updates instantly. Good old AJAX with periodic polling is out — too many useless requests that kill both the server and the user's battery. Two candidates remain: Long Polling and WebSocket.
\n\nBoth solve the "real-time" problem, but they do it in fundamentally different ways. Which one to choose for your Go application? Let's figure it out without fluff, with live code examples and an honest breakdown of pros and cons. This article is your navigator in the world of real-time communications in Go.
\n\nWhat is Long Polling in Go?
\n\nLong Polling is an evolution of regular polling. The client sends an HTTP request to the server, but the server does not respond immediately if there is no data. It "hangs" (keeps the connection open) and waits until a new event occurs. As soon as the event happens — the server sends a response and the client immediately creates a new Long Polling request. The cycle repeats.
\n\nIn Go, this is implemented very elegantly thanks to goroutines and channels. The server can "put to sleep" the request handler until data appears.
\n\nExample of a Long Polling Server in Go
\n\npackage main\n\nimport (\n "fmt"\n "net/http"\n "time"\n)\n\n// Channel where new messages arrive\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, "New message: %s", msg)\n case <-time.After(30 * time.Second):\n // Timeout — if no messages for 30 seconds\n http.Error(w, "Timeout", http.StatusGatewayTimeout)\n }\n}\n\nfunc main() {\n // Goroutine emulating the arrival of new messages\n go func() {\n for {\n time.Sleep(5 * time.Second)\n messages <- fmt.Sprintf("Time: %s", time.Now().String())\n }\n }()\n\n http.HandleFunc("/poll", longPollHandler)\n http.ListenAndServe(":8080", nil)\n}\n\n\nThe client in JavaScript will look like this: after receiving a response (or a timeout), it immediately calls /poll again. Simple, reliable, works through any proxies and firewalls.
WebSocket in Go: Full Duplex
\n\nWebSocket is a protocol that, after a handshake (HTTP Upgrade), turns the connection into a permanent bidirectional channel. The server can send data to the client at any time, and the client to the server. No repeated requests, no timeouts.
\n\nIn Go, the standard library does not have built-in WebSocket support, but there is a gold standard — the gorilla/websocket package. It is stable, well-documented, and used in production by thousands of projects.
Example of a WebSocket Server 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 // For development. In production — check!\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 failed:", err)\n return\n }\n defer conn.Close()\n\n // Infinite loop for reading and writing\n for {\n // Read a message from the client\n _, message, err := conn.ReadMessage()\n if err != nil {\n log.Println("Read error:", err)\n break\n }\n log.Printf("Received: %s", message)\n\n // Send a response (or push notification)\n err = conn.WriteMessage(websocket.TextMessage, []byte("Server: "+time.Now().String()))\n if err != nil {\n log.Println("Write error:", err)\n break\n }\n }\n}\n\nfunc main() {\n http.HandleFunc("/ws", wsHandler)\n log.Pr