Introducción a WebSocket en Go: ¿por qué tiempo real?
Las aplicaciones web modernas requieren cada vez más la entrega instantánea de datos. El tradicional modelo de solicitud/respuesta HTTP ya no es suficiente para tareas como chats, juegos en línea, cotizaciones bursátiles y paneles de monitoreo. Aquí es donde entra en juego WebSocket, un protocolo que proporciona una conexión bidireccional permanente entre el cliente y el servidor. En este artículo, exploraremos cómo crear aplicaciones en tiempo real con Go (Golang) usando WebSocket, desde la instalación hasta soluciones de producción.
Go es ideal para WebSocket gracias a su modelo ligero de concurrencia (gorutinas) y su alto rendimiento. Recorreremos el camino desde un simple servidor eco hasta un chat completo y un sistema de monitoreo.
Fundamentos de WebSocket y configuración del entorno
¿Qué es WebSocket?
WebSocket es un protocolo que funciona sobre TCP. Comienza con un handshake HTTP (upgrade), después del cual la conexión cambia a un canal bidireccional permanente. A diferencia de HTTP, el servidor puede enviar datos al cliente sin una solicitud; esta es la base del tiempo real.
Instalación y bibliotecas
En Go no hay una biblioteca estándar para WebSocket, pero existe la popular biblioteca gorilla/websocket. La instalaremos:
go get github.com/gorilla/websocketTambién se puede usar nhooyr.io/websocket (una alternativa más moderna), pero en este artículo nos centraremos en gorilla por ser la más común.
Primer servidor WebSocket en Go
Escribamos un servidor eco simple que recibe un mensaje y lo envía de vuelta:
package main
import ( "log" "net/http" "github.com/gorilla/websocket")
var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true // para desarrollo },}
func echoHandler(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Print("Upgrade:", err) return } defer conn.Close()
for { mt, message, err := conn.ReadMessage() if err != nil { log.Println("Read:", err) break } log.Printf("Received: %s", message) err = conn.WriteMessage(mt, message) if err != nil { log.Println("Write:", err) break } }}
func main() { http.HandleFunc("/echo", echoHandler) log.Println("Server started on :8080") log.Fatal(http.ListenAndServe(":8080", nil))}Este es un ejemplo funcional mínimo. Observe el Upgrader: verifica los encabezados HTTP y cambia la conexión.
Arquitectura de una aplicación en tiempo real: chat en Go
Gestión de conexiones
Para un chat, necesitamos gestionar múltiples clientes. Crearemos una estructura Hub que almacene todas las conexiones activas y distribuya los mensajes:
type Client struct { hub *Hub conn *websocket.Conn send chan []byte}
type Hub struct { clients map[*Client]bool broadcast chan []byte register chan *Client unregister chan *Client}
func newHub() *Hub { return &Hub{ clients: make(map[*Client]bool), broadcast: make(chan []byte), register: make(chan *Client), unregister: make(chan *Client), }}
func (h *Hub) run() { for { select { case client := <-h.register: h.clients[client] = true case client := <-h.unregister: if _, ok := h.clients[client]; ok { delete(h.clients, client) close(client.send) } case message := <-h.broadcast: for client := range h.clients { select { case client.send <- message: default: