WebSocket en Go: creación de aplicaciones en tiempo real de nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: Más Allá del Simple WebSocket en Go



Los ejemplos básicos de WebSocket en Go suelen mostrar una simple conexión de eco. En proyectos de producción reales, desde terminales de trading hasta juegos multijugador y sistemas de notificación, se requiere mucho más: gestión de salas, apagado graceful, escalabilidad y manejo adecuado de errores. En este artículo, analizaremos un patrón avanzado para construir aplicaciones en tiempo real en Go utilizando la librería gorilla/websocket.



Crearemos una arquitectura basada en Hub (despachador central) y Client (conexión individual), añadiremos soporte para salas, un mecanismo de tolerancia a fallos y mostraremos cómo apagar correctamente el servidor.



Arquitectura Hub y Client: La Base de un Servidor en Tiempo Real



¿Por qué Hub?

Cada cliente WebSocket es una goroutine separada. Gestionarlos directamente desde un manejador HTTP es peligroso: provoca condiciones de carrera y fugas de memoria. El patrón Hub resuelve este problema actuando como un canal único para registro, eliminación y difusión de mensajes.



Implementación del Hub

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 connected. Total: %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 { // Send only to the target room if client.room == message.Room || message.Room == "" { select { case client.send <- message.Data: default: // If the channel is full — disconnect the client h.mu.RUnlock() h.unregister <- client h.mu.RLock() } } } h.mu.RUnlock() } }}


Puntos clave:

  • Channels — register, unregister, broadcast — sincronizan goroutines de forma segura.
  • RWMutex — permite la lectura simultánea de la lista de clientes pero bloquea las escrituras.
  • Enrutamiento basado en salas — un mensaje se envía solo a los clientes en la misma sala.


Gestión de Salas y Suscripciones



Las salas son grupos lógicos de clientes. Por ejemplo, en un chat, cada conversación tiene su propia sala. En nuestro Hub, la sala se define mediante el campo room en Client. Veamos cómo un cliente se suscribe a una sala.



Manejador HTTP con Actualización WebSocket

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

var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true // Para producción, reemplazar con verificación de origen },}

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

Blogs

Book Recommendations