Usado
NodeJS, Socket.io
Problema
Imagina que hay 2 usuarios U1 y U2 , conectados a una aplicación a través de Socket.io. El algoritmo es el siguiente:
- U1 pierde completamente la conexión a Internet (por ejemplo, apaga Internet)
- U2 envía un mensaje a U1 .
- U1 aún no recibe el mensaje, porque Internet no funciona
- El servidor detecta la desconexión de U1 por tiempo de espera de latido
- U1 se vuelve a conectar a socket.io
- U1 nunca recibe el mensaje de U2 ; se pierde en el paso 4, supongo.
Explicación posible
Creo que entiendo por qué sucede:
- en el paso 4, el servidor mata la instancia de socket y la cola de mensajes a U1 también
- Además, en el paso 5, U1 y el servidor crean una nueva conexión (no se reutiliza), por lo que incluso si el mensaje todavía está en cola, la conexión anterior se pierde de todos modos.
Necesitas ayuda
¿Cómo puedo evitar este tipo de pérdida de datos? Tengo que usar Hearbeats, porque la gente no cuelga la aplicación para siempre. También debo dar la posibilidad de volver a conectarme, porque cuando implemento una nueva versión de la aplicación, no quiero tiempo de inactividad.
PD: Lo que llamo "mensaje" no es sólo un mensaje de texto que puedo almacenar en la base de datos, sino un valioso mensaje del sistema, cuya entrega debe garantizarse, o la interfaz de usuario se estropea.
¡Gracias!
Adición 1
Ya tengo un sistema de cuentas de usuario. Además, mi aplicación ya es compleja. Agregar estados fuera de línea / en línea no ayudará, porque ya tengo este tipo de cosas. El problema es diferente.
Consulte el paso 2. En este paso, técnicamente no podemos decir si U1 se desconecta , simplemente pierde la conexión, digamos durante 2 segundos, probablemente debido a una mala conexión a Internet. Entonces U2 le envía un mensaje, pero U1 no lo recibe porque Internet todavía está inactivo para él (paso 3). El paso 4 es necesario para detectar usuarios sin conexión, digamos, el tiempo de espera es de 60 segundos. Finalmente, en otros 10 segundos, la conexión a Internet para U1 se activa y se vuelve a conectar a socket.io. Pero el mensaje de U2 se pierde en el espacio porque el servidor U1 se desconectó por tiempo de espera.
Ese es el problema, no quiero entregar al 100%.
Solución
- Recopile un emisor (nombre de emisión y datos) en {} usuario, identificado por un ID de emisor aleatorio. Enviar emitir
- Confirme la emisión en el lado del cliente (envíe la emisión de vuelta al servidor con emitID)
- Si se confirma, elimina el objeto de {} identificado por emitID
- Si el usuario se volvió a conectar, marque {} para este usuario y recorra el paso ejecutando el Paso 1 para cada objeto en {}
- Cuando se desconecta o se conecta al ras {} para el usuario si es necesario
// Server
const pendingEmits = {};
socket.on('reconnection', () => resendAllPendingLimits);
socket.on('confirm', (emitID) => { delete(pendingEmits[emitID]); });
// Client
socket.on('something', () => {
socket.emit('confirm', emitID);
});
Solución 2 (un poco)
Añadido el 1 de febrero de 2020.
Si bien esta no es realmente una solución para Websockets, es posible que alguien la encuentre útil. Migramos de Websockets a SSE + Ajax. SSE le permite conectarse desde un cliente para mantener una conexión TCP persistente y recibir mensajes de un servidor en tiempo real. Para enviar mensajes de un cliente a un servidor, simplemente use Ajax. Hay desventajas como la latencia y la sobrecarga, pero SSE garantiza la confiabilidad porque es una conexión TCP.
Como usamos Express, usamos esta biblioteca para SSE https://github.com/dpskvn/express-sse , pero puede elegir la que más le convenga.
SSE no es compatible con IE y la mayoría de las versiones de Edge, por lo que necesitaría un polyfill: https://github.com/Yaffle/EventSource .