WebSocket en Java: creación de aplicaciones en tiempo real de nivel avanzado
Las aplicaciones web modernas requieren cada vez más la transferencia instantánea de datos entre el cliente y el servidor. El HTTP tradicional, basado en solicitudes-respuestas, es ineficiente para tareas donde la latencia es crítica: chats en línea, cotizaciones bursátiles, edición colaborativa de documentos o servidores de juegos. El protocolo WebSocket resuelve este problema estableciendo una conexión bidireccional permanente. En este artículo, exploraremos técnicas avanzadas para trabajar con WebSocket en Java, más allá de los ejemplos básicos.
1. Arquitectura de una aplicación WebSocket avanzada en Java
El ejemplo básico de WebSocket en Java usando la anotación @ServerEndpoint es bueno para aprender, pero para soluciones de producción se requiere una arquitectura más robusta. Consideremos los componentes clave.
1.1. Elección del stack tecnológico
Para aplicaciones empresariales, el estándar de facto es Spring Boot con el módulo spring-boot-starter-websocket. Proporciona integración con STOMP (Simple Text Oriented Messaging Protocol) — un subprotocolo sobre WebSocket que añade enrutamiento de mensajes, colas y suscripciones.
1.2. Configuración básica de WebSocket en Spring Boot
Comencemos con la clase de configuración:
@Configuration@EnableWebSocketMessageBrokerpublic class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override public void configureMessageBroker(MessageBrokerRegistry config) { // Broker incorporado para colas /topic y /queue config.enableSimpleBroker("/topic", "/queue"); // Prefijo para mensajes del cliente al servidor config.setApplicationDestinationPrefixes("/app"); // Prefijo para colas de usuario (punto a punto) config.setUserDestinationPrefix("/user"); }
@Override public void registerStompEndpoints(StompEndpointRegistry registry) { // Endpoint para conexión a través de SockJS (fallback para navegadores antiguos) registry.addEndpoint("/ws") .setAllowedOrigins("*") .withSockJS(); }}Este código configura los puntos de conexión, prefijos para enrutamiento y un broker de mensajes incorporado. El uso de STOMP simplifica significativamente el trabajo con suscripciones y colas.
2. Procesamiento avanzado de mensajes: desde chat hasta datos bursátiles
Ahora pasemos a la implementación de escenarios concretos. Consideremos tres casos típicos: chat grupal, notificaciones personales y streaming de datos.
2.1. Chat grupal con historial de mensajes
Un chat simple es una difusión (broadcast) de mensajes en un tópico. Compliquemos la tarea: añadamos almacenamiento de historial y filtrado por salas.
@Controllerpublic class ChatController {
@Autowired private ChatMessageRepository repository; // Repositorio JPA
@MessageMapping("/chat.sendMessage") @SendTo("/topic/chat/{roomId}") public ChatMessage sendMessage(@Payload ChatMessage message, @DestinationVariable String roomId, SimpMessageHeaderAccessor headerAccessor) { // Guardamos en la BD message.setTimestamp(Instant.now()); repository.save(message); // Añadimos información del remitente desde la sesión String username = (String) headerAccessor.getSessionAttributes().get("username"); message.setSender(username); return message; }
@MessageMapping("/chat.addUser") @SendTo("/topic/chat/{roomId}") public ChatMessage addUser(@Payload ChatMessage message, @DestinationVariable String roomId, SimpMessageHeaderAccessor headerAccessor) { headerAccessor.getSessionAttributes().put("username", message.getSender()); return message; }}Aquí se utiliza