Publish/Subscribe: SSE vs WebSockets

CONTEXTO

Vamos a suponer que queremos tener un sistema que informe a todos los clientes de los cambios que se han producido en una BD a través de SSE.

Cada una de las conexiones que realiza un objeto de tipo «EvenSource» al script PHP SSE (suponiendo que estemos usando JS para la parte cliente), provocará que cada uno de los scripts lean de la BD constantemente (estos scripts no comparten memoria ni nada parecido, son procesos independientes entre sí).

No parece muy óptimo ¿verdad? Lo ideal sería que sólo hubiese un proceso leyendo de la BD y este fuese el que le comunicase a los demás scripts SSE que ha habido un cambio (y estos a su vez se lo indicasen a los clientes). O sea, que estamos planteando una comunicación inter-proceso para los scripts de PHP.

Para ello lo más lógico sería tener un socket entre los diferentes SSE y el proceso PHP que está pendiente de los cambios de la BD.

Pero entonces ¿Por qué no poner un WebSocket directamente entre la parte cliente y dicho proceso PHP que está pendiente de los cambios de la BD? Si queremos modularizar (que sería lo más lógico), tendríamos por un lado el proceso PHP que está pendiente de los cambios (que pasaría a ser un servicio), por otro lado tendríamos el servicio PHP que realiza la comunicación inter-proceso mediante sockets (haría la función de «proxy» entre el servicio que lee la BD y los diferentes scripts SSE) y finalmente tendríamos el conjunto de scripts SSE en ejecución.

Con WebSockets, en un sistema publish/subscribe, la arquitectura sería similar, con la diferencia de que los SSE no estarían de intermediaros con los clientes, sino que existirían conexiones WS directamente con el servicio PHP proxy.

Esta arquitectura planteada de publish/subscribe con SSE presenta algunas ventajas e inconvenientes.

VENTAJAS

1.- Los tópicos pasan a ser eventos: la primera ventaja es que, con SSE, los tópicos pasan a ser eventos, y JavaScript ya está preparado para crear una arquitectura orientada a eventos (con WebSockets había que procesar el tópico y derivar los datos por una rama del código u otra en base al nombre del tópico recibido con los datos).

2.- No se requiere sistema de login extra: la segunda es que NO es necesario implementar un sistema de login en los sockets para distinguir quién puede publicar y quién no ya que, la comunicación inter-proceso se realizaría en local y el socket máster del servicio «proxy» se puede configurar para que solo escuche conexiones que vengan de la interfaz de red 127.0.0.1. En ese caso, todos los sockets que se conecten al proxy pueden publicar.

3.- La lógica de «publish» se realiza en otro proceso que además aprovecha las sesiones PHP: A colación de lo anterior, si un usuario (o un servicio externo) quiere publicar (publish), puede hacerlo a través de una petición HTTP (por ejemplo a una entrada de una API REST), lo cual aprovecharía el sistema de login de PHP que tengamos implementado en nuestro sitio/aplicación web, y aprovechando, por ende, la potencia de las sesiones PHP. Es más, este proceso se realizaría en paralelo al hilo de ejecución del «servicio proxy», aprovechando el multithreading que tenga la CPU + SO el servidor (en el sistema de p/s con WebSockets, la carga la lleva un único hilo PHP):

5.- No hay que abrir puertos: No hay que abrir puertos en los routers ya que la comunicación con los clientes iría por HTTP (cuyos puertos ya están abiertos por tener un servidor HTTP en ejecución). Esto redunda en una menor superficie de ataque para usuarios maliciosos.

6.- No hay que configurar los sockets para que la conexión sea segura, con tener una conexión HTTPS es suficiente.

7.- La reconexión con el SSE se realiza automáticamente.

DESVENTAJAS

1.- Menor compatibilidad: No hay soporte para SSE en IE ni en Edge (pero sí hay soporte para WebSockets), aunque esto se puede arreglar fácilmente usando alguna biblioteca polyfills (como ESTA).

2.- Menor número de conexiones: Cada conexión a un SSE de un dominio implica que hay menos conexiones HTTP disponibles a dicho dominio, y estas suelen estar en torno a 6 conexiones por dominio (dependiendo del navegador), mientras que el límite de los WebSockets está en torno a las 1024 conexiones. La solución sería usar otros nombres de dominio para la lógica de los SSE o limitar el número de SSE por cliente mediante algún contador en la parte cliente o servidora.

3.- Debemos descapar el timeOut: Hay que configurar el servidor HTTP y/o el intérprete de PHP para que permita que los scripts SSE se ejecuten indefinidamente.

4.- El tiempo de respuesta es mayor: Para realizar una publicación (publish), hay que realizar una conexión HTTP por cada una de las publicaciones que hagamos. Con WebSockets la conexión ya está abierta y no hay que esperar a que esta se establezca.

5.- Debemos garantizar FIFO desde el código cliente: los WebSockets garantizan que los datos se envían y procesan en orden, mientras que las peticiones AJAX no.

CONCLUSIÓN

La conclusión sería que no hay ganador como tal, ya que SSE no puede vivir sin los sockets para la comunicación inter-proceso, pero con SSE no tenemos que implementar ningún sistema de login extra (ya que la comunicación es unidireccional).

Es cierto que usar SSE nos obliga a utilizar peticiones AJAX, y estas aprovechan las sesiones de PHP, así que tampoco tendremos que realizar login cada vez que un servicio o un usuario desea publicar, solo hay que determinar dentro de la lógica del backend de la petición AJAX si ese usuario concreto que quiere publicar puede realizar dicha acción.

Explotación de recursos.

Realizar la publicación por AJAX descarga y modulariza la lógica del hilo único de ejecución del servicio PHP de p/s (que en este caso lo hemos denominado «proxy»). Cada hilo de ejecución de PHP puede consumir un máximo de recursos (aunque esto también depende de la configuración), de esta manera, al ejecutar la lógica de publicación en un script PHP separado del hilo del «proxy» estamos aprovechando mejor los recursos que nos brinda el Hardware y el SO del servidor (como por ejemplo el multithreading).

Usar peticiones AJAX para publicar también se puede hacer usando WebSockets, pero sería un poco absurdo ya que estos permiten comunicación bidireccional.

Además, el servicio p/s de PHP (el denominado «proxy» en el ejemplo planteado) también puede ser ejecutado con parámetros de recursos personalizados (por ejemplo aumentando la memoria máxima con una opción en el comando: «php.exe -d memory_limit=1GB ServicioPS.php»).

Tiempos de respuesta.

Sin embargo, este sistema para realizar publish por AJAX, implicará un mayor tiempo de respuesta que se requiere para realizar la conexión HTTP con el servidor ya que la conexión del WebSocket se mantiene abierta y no requiere de este proceso de conexión reiterado (cuyo tiempo de respuesta aumenta si además usamos conexiones seguras).

Por tanto, en cuanto a tiempos de respuesta, solo sería ganador SSE si el servicio externo (que en este caso lee cambios de una BD) puede logearse (crear una sesión PHP) pero no puede crear un socket. En otro caso, el mejor tiempo de respuesta lo obtendríamos al mantener la conexión abierta con un WebSocket.

Seguridad

En cuanto a seguridad ganaría SSE ya que solo hay que implementar y centrarse en un sistema de login basado en cookie de sesión (cuya implantación de medidas de seguridad para este sistema está ya muy documentada en la comunidad). Como no hay que crear un sistema de login para los sockets (porque se crean y se conectan en la máquina local) considero que en este aspecto destaca bastante SSE.

Pero en contraposición a esto último, hay que poner mecanismos para restringir el número de SSE’s abiertos por usuario para que no devore todos las conexiones HTTP disponibles al dominio.


Créditos de fuentes externas:

Iconos:

  • PC by art shop from the Noun Project
  • Server by Chanut is Industries from the Noun Project
  • Cloud by AlePio from the Noun Project
  • Gears by Gregor Cresnar from the Noun Project
  • Database by IcoMoon from the Noun Project

Referencias:

  • https://superuser.com/questions/732218/what-is-phps-memory-limit
  • https://stackoverflow.com/questions/1425138/increasing-php-memory-limit-at-what-point-does-it-become-insane

Deja una respuesta