Gebraucht
NodeJS, Socket.io
Problem
Stellen Sie sich vor, es gibt zwei Benutzer U1 und U2 , die über Socket.io mit einer App verbunden sind. Der Algorithmus ist der folgende:
- U1 verliert die Internetverbindung vollständig (z. B. schaltet das Internet aus)
- U2 sendet eine Nachricht an U1 .
- U1 empfängt die Nachricht noch nicht, da das Internet nicht verfügbar ist
- Der Server erkennt die U1- Trennung durch Heartbeat-Timeout
- U1 stellt die Verbindung zu socket.io wieder her
- U1 empfängt die Nachricht nie von U2 - sie geht in Schritt 4 verloren, denke ich.
Mögliche Erklärung
Ich glaube ich verstehe warum es passiert:
- Schritt 4 auf Server tötet Socket - Instanz und die Warteschlange von Nachrichten an U1 sowie
- Darüber hinaus erstellen U1 und Server in Schritt 5 eine neue Verbindung (diese wird nicht wiederverwendet). Selbst wenn sich die Nachricht noch in der Warteschlange befindet, geht die vorherige Verbindung trotzdem verloren.
Brauchen Sie Hilfe
Wie kann ich diese Art von Datenverlust verhindern? Ich muss Hearbeats verwenden, weil ich nicht für immer Leute in der App hängen habe. Außerdem muss ich immer noch die Möglichkeit geben, die Verbindung wiederherzustellen, da ich bei der Bereitstellung einer neuen App-Version keine Ausfallzeiten möchte.
PS Das, was ich "Nachricht" nenne, ist nicht nur eine Textnachricht, die ich in der Datenbank speichern kann, sondern eine wertvolle Systemnachricht, deren Zustellung garantiert werden muss, oder die Benutzeroberfläche ist fehlerhaft.
Vielen Dank!
Zusatz 1
Ich habe bereits ein Benutzerkontosystem. Darüber hinaus ist meine Bewerbung bereits komplex. Das Hinzufügen von Offline- / Online-Status hilft nicht weiter, da ich solche Dinge bereits habe. Das Problem ist anders.
Check out Schritt 2. In diesem Schritt können wir technisch nicht sagen, ob U1 offline geht , er verliert nur die Verbindung, sagen wir für 2 Sekunden, wahrscheinlich wegen schlechten Internets. Also sendet U2 ihm eine Nachricht, aber U1 empfängt sie nicht, weil das Internet für ihn immer noch nicht verfügbar ist (Schritt 3). Schritt 4 ist erforderlich, um Offline-Benutzer zu erkennen. Das Zeitlimit beträgt beispielsweise 60 Sekunden. In weiteren 10 Sekunden ist die Internetverbindung für U1 hergestellt und er stellt die Verbindung zu socket.io wieder her. Die Nachricht von U2 geht jedoch im Speicherplatz verloren, da auf dem Server U1 durch Timeout die Verbindung getrennt wurde.
Das ist das Problem, ich will nicht 100% liefern.
Lösung
- Sammeln Sie eine Emission (Emit Name und Daten) in {} Benutzer, identifiziert durch zufällige emitID. Senden senden
- Bestätigen Sie die Ausgabe auf der Clientseite (senden Sie die Ausgabe mit der EmitID an den Server zurück).
- Wenn bestätigt - Objekt aus {} löschen, das durch emitID identifiziert wurde
- Wenn der Benutzer erneut verbunden ist - überprüfen Sie {} für diesen Benutzer und führen Sie eine Schleife durch, indem Sie Schritt 1 für jedes Objekt in {} ausführen
- Wenn getrennt oder / und verbunden, spülen Sie {} für den Benutzer, falls erforderlich
// Server
const pendingEmits = {};
socket.on('reconnection', () => resendAllPendingLimits);
socket.on('confirm', (emitID) => { delete(pendingEmits[emitID]); });
// Client
socket.on('something', () => {
socket.emit('confirm', emitID);
});
Lösung 2 (irgendwie)
Hinzugefügt am 1. Februar 2020.
Dies ist zwar keine wirkliche Lösung für Websockets, aber jemand findet sie möglicherweise dennoch praktisch. Wir sind von Websockets auf SSE + Ajax migriert. Mit SSE können Sie eine Verbindung von einem Client aus herstellen, um eine dauerhafte TCP-Verbindung aufrechtzuerhalten und Nachrichten von einem Server in Echtzeit zu empfangen. Um Nachrichten von einem Client an einen Server zu senden, verwenden Sie einfach Ajax. Es gibt Nachteile wie Latenz und Overhead, aber SSE garantiert Zuverlässigkeit, da es sich um eine TCP-Verbindung handelt.
Da wir Express verwenden, verwenden wir diese Bibliothek für SSE https://github.com/dpskvn/express-sse , aber Sie können diejenige auswählen, die zu Ihnen passt.
SSE wird in IE und den meisten Edge-Versionen nicht unterstützt, daher benötigen Sie eine Polyfüllung: https://github.com/Yaffle/EventSource .