Blog

Websockets en Laravel sin mantener un servidor websocket

· Andrij Tkachenko · 5 min de lectura

Una ventana de editor de código unida por una línea a una nube, que envía actualizaciones en directo a cuatro ventanas de navegador

Laravel hace que las actualizaciones en directo parezcan fáciles. Un evento implementa ShouldBroadcast, routes/channels.php dice quién puede escuchar y Laravel Echo lo recibe en el navegador. Un pedido pasa a enviado y el cliente lo ve sin recargar la página.

Lo que la documentación solo menciona de pasada es que algo tiene que mantener la conexión con cada navegador abierto. Laravel en sí responde a peticiones y se detiene, así que el broadcasting siempre necesita un servidor websocket junto a tu app. Este artículo explica por qué, qué hace falta para mantener uno en producción y cómo conservar tus eventos y Echo exactamente como están mientras una nube en tiempo real mantiene las conexiones por ti.

Por qué Laravel necesita un servidor websocket

Una petición en Laravel es corta. PHP recibe la petición, Laravel arranca, tu controlador responde y el proceso queda libre para la siguiente. Eso es lo que hace que Laravel sea rápido y fácil de alojar, y también es la razón por la que una petición no puede quedarse abierta durante horas para enviar actualizaciones a un navegador.

Por eso el broadcasting tiene dos mitades:

  • Tu app envía. Cuando llamas a broadcast( new OrderUpdated( $order ) ), Laravel entrega el evento a un broadcaster, que lo reenvía en una petición corta. Con ShouldBroadcast eso ocurre en un job en cola, así que lo ejecuta un worker de colas.
  • Otra pieza lo entrega. Un servidor websocket mantiene una conexión abierta con cada navegador, consulta a tu app quién puede unirse a cada canal privado y pasa cada evento a los navegadores que escuchan.

La primera mitad es Laravel de siempre. La segunda es un servicio aparte que funciona sin parar, y ahí está el trabajo.

Mantener tu propio servidor websocket

Laravel tiene un servidor websocket que puedes ejecutar tú mismo, y otros proyectos también. Arrancar uno en tu portátil lleva un minuto. Mantenerlo para usuarios reales exige más:

  • Un proceso que funciona sin parar, mantenido vivo por un gestor de procesos, reiniciado cuando se cae, con sus propios logs y su propia monitorización. El hosting compartido y las plataformas serverless no pueden ejecutarlo en absoluto.
  • Un puerto, un certificado y un proxy, para que los navegadores puedan conectarse por wss:// en tu dominio, con el proxy configurado para dejar pasar los websockets y no cortar las conexiones largas.
  • Capacidad para muchas conexiones. Cada navegador abierto es una conexión abierta, y el servidor y el sistema operativo necesitan límites ajustados para miles de ellas a la vez.
  • Más de un servidor, a medida que creces. Un solo servidor es un único punto en el que pueden caerse todas las conexiones. Con dos o más hace falta un canal compartido, normalmente Redis, para que un evento enviado a uno llegue a los navegadores conectados a los demás.
  • Deploys sin avalancha. Reiniciar el servidor websocket corta todas las conexiones, y todos los navegadores se reconectan a la vez. Además, las páginas que estaban desconectadas se pierden lo que se envió mientras tanto, a menos que algo lo guarde para ellas.

Nada de esto es exótico, y muchos equipos lo hacen bien. Pero es un segundo sistema que hay que mantener en marcha junto a tu app mientras tenga funciones en directo.

O deja que una nube en tiempo real mantenga las conexiones

La otra vía es usar un servicio alojado como broadcaster. Tu app envía cada evento en una petición HTTP corta, como ya hace con un broadcaster, y el servicio mantiene las conexiones, comprueba las reglas de tus canales a través de tu app y entrega los eventos. Tu app de Laravel sigue siendo una app de Laravel normal, en cualquier hosting, serverless incluido.

Lo importante es que el cambio se queda dentro del propio broadcasting de Laravel, así que tus eventos, tus canales y tu código de Echo no cambian.

El broadcasting de Laravel en WordPlus Cloud

Con WordPlus Cloud, el cambio es una conexión de broadcasting. Tu .env la indica:

.env
BROADCAST_CONNECTION=wordplus
WORDPLUS_SITE=s1500000007
WORDPLUS_SECRET=…
WORDPLUS_LOCAL_KEY=…

Tus canales siguen en routes/channels.php, con un argumento opcional para lo que guarda cada sala:

PHP
// The room of an order, orders.1042, for its customer, the last 20 updates kept.
Broadcast::channel( 'orders.{order}', fn ( User $user, Order $order ) => $user->id === $order->customer_id, [ 'history' => 20 ] );

Tus eventos se quedan como están, y broadcast( new OrderUpdated( $order ) ) los envía. En el navegador, Echo usa nuestro conector en lugar del habitual:

JavaScript
import Echo from 'laravel-echo';
import { WordplusConnector } from '@wordplus/realtime/echo';

window.Echo = new Echo( { broadcaster: WordplusConnector, ...window.wordplusRealtimeConfig } );

Echo.private( 'orders.1042' ).listen( '.updated', ( data ) => showStatus( data.status ) );

La directiva @wordplusRealtime de tu layout escribe esa configuración, con el usuario que ha iniciado sesión, así que la página se conecta sin ninguna petición previa. A partir de ahí funciona todo lo que conoces de Echo:

  • Canales privados y de presencia, con here(), joining() y leaving() para saber quién está en una sala, whisper() para mostrar quién está escribiendo y notification() para las notificaciones de Laravel.
  • Livewire recibe los eventos igual, con #[On( 'echo-private:orders.{order.id},.updated' )].
  • React y Vue usan useEcho() de los propios paquetes de Echo de Laravel, como hacen los starter kits.
  • toOthers() omite la pestaña que envió la petición.

Y lo que un servidor websocket por sí solo no te da:

  • Ningún servidor websocket que mantener. Nuestros servidores mantienen las conexiones, se arrancan más a medida que crece tu app y, cuando uno se reinicia, la página pasa a otro sin cortes.
  • Nada se pierde al reconectar. Una sala guarda sus últimos eventos, así que una página que vuelve recibe lo que se perdió, en orden.
  • Sellado en tránsito. Lo que envía tu app viaja cifrado con claves que solo tienen tu app y tus usuarios.
  • Llamadas y transmisiones en directo sobre la misma conexión. Llamadas entre dos personas, llamadas en grupo y transmisiones en directo, con una pantalla de llamada y un reproductor listos para usar, o los tuyos.

La guía de Laravel tiene toda la configuración, y la demo en vivo es una pequeña tienda construida sobre ella, en la que un pedido pasa a enviado en otra ventana en cuanto haces clic.

WordPlus Cloud for Apps tiene un precio según las conexiones simultáneas, a partir de 1.000, con facturación mensual o anual. Descubre lo que hace para apps, o consulta los precios para apps. ¿Preguntas sobre tu integración? Escríbenos.