Zuverlässigkeit des Websocket-Transports (Datenverlust von Socket.io während der erneuten Verbindung)

81

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:

  1. U1 verliert die Internetverbindung vollständig (z. B. schaltet das Internet aus)
  2. U2 sendet eine Nachricht an U1 .
  3. U1 empfängt die Nachricht noch nicht, da das Internet nicht verfügbar ist
  4. Der Server erkennt die U1- Trennung durch Heartbeat-Timeout
  5. U1 stellt die Verbindung zu socket.io wieder her
  6. 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

  1. Sammeln Sie eine Emission (Emit Name und Daten) in {} Benutzer, identifiziert durch zufällige emitID. Senden senden
  2. Bestätigen Sie die Ausgabe auf der Clientseite (senden Sie die Ausgabe mit der EmitID an den Server zurück).
  3. Wenn bestätigt - Objekt aus {} löschen, das durch emitID identifiziert wurde
  4. 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
  5. 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 .

igorpavlov
quelle
Stimmt das. Aber socket.io ist wirklich nur ein Transportprotokoll. Es allein kann keine konsistente und zuverlässige Nachrichtenübermittlung garantieren. Sie sollten sich die Architekturen und Nachrichtenwarteschlangen von Pub-Sub (Publish-Subscribe) ansehen (und lesen). In der Praxis verwenden Sie eine persistente Datenbank wie redis zum Speichern von Nachrichten.
user568109
Pubsub wird dieses Problem also lösen? Wenn Sie eine umfassende Antwort schreiben und die Lösung funktioniert, werden Sie mit einem Kopfgeld (50 Punkte) belohnt.
Igorpavlov
8
so eine schön organisierte Frage
Katie
1
Vielen Dank. Ich muss sagen, dass die akzeptierte Antwort für mich funktioniert. Ich verwende derzeit das vorgeschlagene Schema und habe keine Probleme.
Igorpavlov
Hallo Igor! Ich bin neu bei Node.js und Socket.io. Wenn es möglich ist, könnten Sie Ihren Code zeigen :)
Eazy

Antworten:

102

Andere haben dies in anderen Antworten und Kommentaren angedeutet, aber das Hauptproblem besteht darin, dass Socket.IO nur ein Übermittlungsmechanismus ist und Sie sich für eine zuverlässige Übermittlung nicht allein darauf verlassen können. Die einzige Person, die sicher weiß, dass eine Nachricht erfolgreich an den Client übermittelt wurde, ist der Client selbst . Für diese Art von System würde ich die folgenden Aussagen empfehlen:

  1. Nachrichten werden nicht direkt an Clients gesendet. Stattdessen werden sie an den Server gesendet und in einer Art Datenspeicher gespeichert.
  2. Clients sind dafür verantwortlich, beim erneuten Herstellen der Verbindung zu fragen, was ich verpasst habe, und fragen die im Datenspeicher gespeicherten Nachrichten ab, um ihren Status zu aktualisieren.
  3. Wenn eine Nachricht an den Server gesendet wird, während der Empfängerclient verbunden ist , wird diese Nachricht in Echtzeit an den Client gesendet.

Abhängig von den Anforderungen Ihrer Anwendung können Sie natürlich Teile davon optimieren. Sie können beispielsweise eine Redis-Liste oder einen sortierten Satz für die Nachrichten verwenden und diese löschen, wenn Sie wissen, dass ein Client aktiv ist miteinander ausgehen.


Hier einige Beispiele:

Glücklicher Weg :

  • U1 und U2 sind beide mit dem System verbunden.
  • U2 sendet eine Nachricht an den Server, die U1 empfangen soll.
  • Der Server speichert die Nachricht in einem dauerhaften Speicher und markiert sie für U1 mit einem Zeitstempel oder einer sequentiellen ID.
  • Der Server sendet die Nachricht über Socket.IO an U1.
  • Der Client von U1 bestätigt (möglicherweise über einen Socket.IO-Rückruf), dass er die Nachricht empfangen hat.
  • Der Server löscht die persistierte Nachricht aus dem Datenspeicher.

Offline-Pfad :

  • U1 verliert die Internetverbindung.
  • U2 sendet eine Nachricht an den Server, die U1 empfangen soll.
  • Der Server speichert die Nachricht in einem dauerhaften Speicher und markiert sie für U1 mit einem Zeitstempel oder einer sequentiellen ID.
  • Der Server sendet die Nachricht über Socket.IO an U1.
  • U1 des Kunden nicht bestätigen Empfang, weil sie offline sind.
  • Vielleicht sendet U2 U1 noch ein paar Nachrichten; Sie werden alle auf dieselbe Weise im Datenspeicher gespeichert.
  • Wenn U1 erneut eine Verbindung herstellt, wird der Server gefragt: "Die letzte Nachricht, die ich gesehen habe, war X / Ich habe den Status X, was habe ich verpasst."
  • Der Server sendet U1 alle Nachrichten, die er aufgrund der Anforderung von U1 aus dem Datenspeicher verpasst hat
  • Der Client von U1 bestätigt den Empfang und der Server entfernt diese Nachrichten aus dem Datenspeicher.

Wenn Sie unbedingt eine garantierte Lieferung wünschen, ist es wichtig, Ihr System so zu gestalten, dass die Verbindung keine Rolle spielt und die Lieferung in Echtzeit einfach ein Bonus ist . Dabei handelt es sich fast immer um einen Datenspeicher. Wie der Benutzer 568109 in einem Kommentar erwähnt hat, gibt es Nachrichtensysteme, die die Speicherung und Zustellung dieser Nachrichten abstrahieren, und es kann sich lohnen, eine solche vorgefertigte Lösung zu prüfen. (Sie müssen die Socket.IO-Integration wahrscheinlich noch selbst schreiben.)

Wenn Sie nicht daran interessiert sind, die Nachrichten in der Datenbank zu speichern, können Sie sie möglicherweise in einem lokalen Array speichern. Der Server versucht, U1 die Nachricht zu senden, und speichert sie in einer Liste "ausstehender Nachrichten", bis der Client von U1 bestätigt, dass er sie empfangen hat. Wenn der Client offline ist, kann er dem Server bei seiner Rückkehr mitteilen, dass "Hey, ich wurde getrennt, bitte senden Sie mir alles, was ich verpasst habe", und der Server kann diese Nachrichten durchlaufen.

Glücklicherweise bietet Socket.IO einen Mechanismus, mit dem ein Client auf eine Nachricht "antworten" kann, die wie native JS-Rückrufe aussieht. Hier ist ein Pseudocode:

// server
pendingMessagesForSocket = [];

function sendMessage(message) {
  pendingMessagesForSocket.push(message);
  socket.emit('message', message, function() {
    pendingMessagesForSocket.remove(message);
  }
};

socket.on('reconnection', function(lastKnownMessage) {
  // you may want to make sure you resend them in order, or one at a time, etc.
  for (message in pendingMessagesForSocket since lastKnownMessage) {
    socket.emit('message', message, function() {
      pendingMessagesForSocket.remove(message);
    }
  }
});

// client
socket.on('connection', function() {
  if (previouslyConnected) {
    socket.emit('reconnection', lastKnownMessage);
  } else {
    // first connection; any further connections means we disconnected
    previouslyConnected = true;
  }
});

socket.on('message', function(data, callback) {
  // Do something with `data`
  lastKnownMessage = data;
  callback(); // confirm we received the message
});

Dies ist dem letzten Vorschlag ziemlich ähnlich, einfach ohne einen dauerhaften Datenspeicher.


Vielleicht interessieren Sie sich auch für das Konzept des Event Sourcing .

Michelle Tilley
quelle
2
Ich habe auf die endgültige umfassende Antwort mit einer Erklärung gewartet, dass Kunden die Lieferung bestätigen MÜSSEN. Es scheint wirklich keinen anderen Weg zu geben.
Igorpavlov
Glücklich geholfen zu haben! Gib mir einen Ping, wenn du irgendwelche Fragen hast.
Michelle Tilley
Dies funktioniert in einem Eins-zu-Eins-Chat-Szenario. Was passiert beispielsweise in Räumen, in denen Nachrichten an mehrere Benutzer gesendet werden? Broadcast / Socket.in unterstützt keinen Rückruf. Wie gehen wir mit dieser Situation um? meine Frage dazu. ( stackoverflow.com/questions/43186636/… )
Jit
2

Michelles Antwort ist ziemlich zutreffend, aber es gibt noch ein paar andere wichtige Dinge zu beachten. Die Hauptfrage, die Sie sich stellen müssen, lautet: "Gibt es einen Unterschied zwischen einem Benutzer und einem Socket in meiner App?" Eine andere Möglichkeit zu fragen ist: "Kann jeder angemeldete Benutzer mehr als eine Socket-Verbindung gleichzeitig haben?"

In der Web-Welt ist es wahrscheinlich immer möglich, dass ein einzelner Benutzer mehrere Socket-Verbindungen hat, es sei denn, Sie haben speziell etwas eingerichtet, das dies verhindert. Das einfachste Beispiel hierfür ist, wenn ein Benutzer zwei Registerkarten derselben Seite geöffnet hat. In diesen Fällen ist es Ihnen nicht wichtig, eine Nachricht / ein Ereignis nur einmal an den menschlichen Benutzer zu senden. Sie müssen sie an jede Socket-Instanz für diesen Benutzer senden, damit jede Registerkarte ihre Rückrufe ausführen kann, um den UI-Status zu aktualisieren. Vielleicht ist dies für bestimmte Anwendungen kein Problem, aber mein Bauch sagt, dass es für die meisten sein würde. Wenn dies ein Problem für Sie ist, lesen Sie weiter ....

Um dies zu lösen (vorausgesetzt, Sie verwenden eine Datenbank als dauerhaften Speicher), benötigen Sie 3 Tabellen.

  1. Benutzer - das ist eine 1 zu 1 mit echten Menschen
  2. Kunden - das ist ein „Register“ darstellt, könnte eine einzige Verbindung zu einem Socket - Server hat. (Jeder 'Benutzer' kann mehrere haben)
  3. Nachrichten - Eine Nachricht, die an einen Client gesendet werden muss (keine Nachricht, die an einen Benutzer oder an einen Socket gesendet werden muss).

Die Benutzertabelle ist optional, wenn Ihre App sie nicht benötigt, das OP jedoch angibt, dass sie eine hat.

Die andere Sache, die richtig definiert werden muss, ist "Was ist eine Socket-Verbindung?", "Wann wird eine Socket-Verbindung erstellt?", "Wann wird eine Socket-Verbindung wiederverwendet?". Der Psudocode von Michelle lässt den Eindruck entstehen, dass eine Socket-Verbindung wiederverwendet werden kann. Mit Socket.IO können sie NICHT wiederverwendet werden. Ich habe gesehen, dass es viel Verwirrung stiftet. Es gibt reale Szenarien, in denen Michelles Beispiel Sinn macht. Aber ich muss mir vorstellen, dass diese Szenarien selten sind. Was wirklich passiert, ist, wenn eine Socket-Verbindung unterbrochen wird, diese Verbindung, ID usw. niemals wiederverwendet werden. Nachrichten, die speziell für diesen Socket markiert sind, werden daher niemals an Dritte gesendet, da der Client, der ursprünglich eine Verbindung hergestellt hat, erneut eine Verbindung herstellt und eine völlig neue Verbindung und eine neue ID erhält. Das heißt es '

Für ein webbasiertes Beispiel wären hier die Schritte, die ich empfehlen würde:

  • Wenn ein Benutzer einen Client (normalerweise eine einzelne Webseite) lädt, der möglicherweise eine Socket-Verbindung herstellen kann, fügen Sie der Client-Datenbank eine Zeile hinzu, die mit seiner Benutzer-ID verknüpft ist.
  • Wenn der Benutzer tatsächlich eine Verbindung zum Socket-Server herstellt, übergeben Sie die Client-ID mit der Verbindungsanforderung an den Server.
  • Der Server sollte überprüfen, ob der Benutzer eine Verbindung herstellen darf und die Clientzeile in der Clienttabelle für die Verbindung verfügbar ist, und entsprechend zulassen / verweigern.
  • Aktualisieren Sie die Clientzeile mit der von Socket.IO generierten Socket-ID.
  • Senden Sie alle Elemente in der Nachrichtentabelle, die mit der Client-ID verbunden sind. Bei der ersten Verbindung würde es keine geben, aber wenn dies vom Client stammt, der versucht, die Verbindung wiederherzustellen, kann es einige geben.
  • Fügen Sie jedes Mal, wenn eine Nachricht an diesen Socket gesendet werden muss, eine Zeile in die Nachrichtentabelle ein, die mit der von Ihnen generierten Client-ID verknüpft ist (nicht mit der Socket-ID).
  • Versuchen Sie, die Nachricht zu senden und mit der Bestätigung auf den Client zu warten.
  • Wenn Sie die Bestätigung erhalten, löschen Sie dieses Element aus der Nachrichtentabelle.
  • Möglicherweise möchten Sie auf der Clientseite eine Logik erstellen, die doppelte vom Server gesendete Nachrichten verwirft, da dies technisch eine Möglichkeit ist, wie einige bereits betont haben.
  • Wenn sich ein Client (absichtlich oder fehlerhaft) vom Socket-Server trennt, löschen Sie NICHT die Client-Zeile, sondern löschen Sie höchstens die Socket-ID. Dies liegt daran, dass derselbe Client versuchen könnte, die Verbindung wiederherzustellen.
  • Wenn ein Client versucht, die Verbindung wiederherzustellen, senden Sie dieselbe Client-ID, die er beim ursprünglichen Verbindungsversuch gesendet hat. Der Server sieht dies wie eine erste Verbindung an.
  • Wenn der Client zerstört wird (Benutzer schließt die Registerkarte oder navigiert weg), löschen Sie die Clientzeile und alle Nachrichten für diesen Client. Dieser Schritt kann etwas schwierig sein.

Weil der letzte Schritt schwierig ist (zumindest war es früher so, ich habe so etwas schon lange nicht mehr gemacht) und weil es Fälle wie Stromausfall gibt, in denen der Client die Verbindung trennt, ohne die Client-Zeile zu bereinigen, und es nie versucht Um die Verbindung mit derselben Clientzeile wiederherzustellen, möchten Sie wahrscheinlich etwas, das regelmäßig ausgeführt wird, um veraltete Client- und Nachrichtenzeilen zu bereinigen. Oder Sie können einfach alle Clients und Nachrichten für immer dauerhaft speichern und ihren Status entsprechend markieren.

Um klar zu sein, fügen Sie in Fällen, in denen ein Benutzer zwei Registerkarten geöffnet hat, der Nachrichtentabelle zwei identische Nachrichten hinzu, die jeweils für einen anderen Client markiert sind, da Ihr Server wissen muss, ob jeder Client sie empfangen hat, nicht nur jeder Benutzer.

Ryan
quelle
1

Es scheint, dass Sie bereits ein Benutzerkontosystem haben. Sie wissen, welches Konto online / offline ist. Sie können das Ereignis zum Verbinden / Trennen behandeln:

Die Lösung besteht also darin, für jeden Benutzer Online- / Offline- und Offline-Nachrichten zur Datenbank hinzuzufügen:

chatApp.onLogin(function (user) {
   user.readOfflineMessage(function (msgs) {
       user.sendOfflineMessage(msgs, function (err) {
           if (!err) user.clearOfflineMessage();
       });
   })
});

chatApp.onMessage(function (fromUser, toUser, msg) {
   if (user.isOnline()) {
      toUser.sendMessage(msg, function (err) {
          // alert CAN NOT SEND, RETRY?
      });
   } else {
      toUser.addToOfflineQueue(msg);
   }
})
Damphat
quelle
Bitte lesen Sie den Abschnitt "Ergänzung 1" in meiner Frage. Ich denke nicht, dass Ihre Antwort eine Lösung ist.
Igorpavlov
Das ist interessant, ich
starte
Meins auch auf WebRTC. Aber in diesem Zusammenhang spielt es keine Rolle. Ah ... Wenn alle Leute ein stabiles Internet haben ... Ich bin so frustriert, wenn Benutzer 100 Mbit / s auf Speedtest haben, aber tatsächlich, wenn sie versuchen zu pingen, haben sie 20% Paketverlust. Wer braucht so ein Internet? =)
Igorpavlov
0

Schauen Sie hier: Behandeln Sie Browser reload socket.io .

Ich denke, Sie könnten eine Lösung verwenden, die ich mir ausgedacht habe. Wenn Sie es richtig ändern, sollte es funktionieren, wie Sie wollen.

Sind
quelle
Das ist interessant, ich konnte diese Frage nicht finden, habe aber mehrere Stunden gegoogelt. Werde mal schauen!
Igorpavlov
Es scheint, dass ich diese Art von Architektur bereits benutze. Es löst nicht das genaue Problem, das ich beschrieben habe.
Igorpavlov
0

Ich denke, Sie möchten für jeden Benutzer einen wiederverwendbaren Socket haben, etwa:

Klient:

socket.on("msg", function(){
    socket.send("msg-conf");
});

Server:

// Add this socket property to all users, with your existing user system
user.socket = {
    messages:[],
    io:null
}
user.send = function(msg){ // Call this method to send a message
    if(this.socket.io){ // this.io will be set to null when dissconnected
        // Wait For Confirmation that message was sent.
        var hasconf = false;
        this.socket.io.on("msg-conf", function(data){
            // Expect the client to emit "msg-conf"
            hasconf = true;
        });
        // send the message
        this.socket.io.send("msg", msg); // if connected, call socket.io's send method
        setTimeout(function(){
            if(!hasconf){
                this.socket = null; // If the client did not respond, mark them as offline.
                this.socket.messages.push(msg); // Add it to the queue
            }
        }, 60 * 1000); // Make sure this is the same as your timeout.

    } else {
        this.socket.messages.push(msg); // Otherwise, it's offline. Add it to the message queue
    }
}
user.flush = function(){ // Call this when user comes back online
    for(var msg in this.socket.messages){ // For every message in the queue, send it.
        this.send(msg);
    }
}
// Make Sure this runs whenever the user gets logged in/comes online
user.onconnect = function(socket){
    this.socket.io = socket; // Set the socket.io socket
    this.flush(); // Send all messages that are waiting
}
// Make sure this is called when the user disconnects/logs out
user.disconnect = function(){
    self.socket.io = null; // Set the socket to null, so any messages are queued not send.
}

Dann bleibt die Socket-Warteschlange zwischen den Verbindungsabbrüchen erhalten.

Stellen Sie sicher, dass die socketEigenschaften jedes Benutzers in der Datenbank gespeichert werden, und machen Sie die Methoden zu einem Teil Ihres Benutzerprototyps. Die Datenbank spielt keine Rolle, speichern Sie sie einfach, obwohl Sie Ihre Benutzer gespeichert haben.

Dadurch wird das in Additon 1 erwähnte Problem vermieden, indem eine Bestätigung vom Client angefordert wird, bevor die Nachricht als gesendet markiert wird. Wenn Sie wirklich wollten, können Sie jeder Nachricht eine ID geben und den Client die Nachrichten-ID an senden lassen und diese msg-confdann überprüfen.

In diesem Beispiel userhandelt es sich um den Vorlagenbenutzer, von dem alle Benutzer kopiert wurden, oder um den Benutzerprototyp.

Hinweis: Dies wurde nicht getestet.

Ari Porad
quelle
Können Sie mir sagen, was eigentlich die "Benutzervariable" ist?
Igorpavlov
Eigentlich denke ich, dass Sie meine Frage genagelt haben. Aber können Sie bitte auch einige Kommentare zu jedem Code abgeben? Ich verstehe noch nicht, wie ich es in meinen Code integrieren kann. Auch wo soll ich es in der Datenbank speichern und welche Art von Datenbank meinen Sie? Redis oder könnte Mongo sein oder spielt keine Rolle?
Igorpavlov
Es löst das Problem immer noch nicht. Wenn eine Nachricht gesendet wird, sind beide Benutzer (Absender und Empfänger) ONLINE für den Server. Bitte lesen Sie Zusatz 1 in meiner Frage sehr sorgfältig durch. In diesem Fall ist "this.socket.io" immer "true", sodass eine Nachricht gesendet, aber nicht empfangen wird. Sie versuchen, das Problem zu lösen, wenn SENDER offline geht, aber nicht RECEIVER. Oder habe ich nicht recht
Igorpavlov
@igorpavlov, Sorry, aber du missverstehst mich. Stellen Sie sich Folgendes vor: U1 möchte eine Nachricht "Hallo" an U2 senden : users.getUserByName("U2").send("Hi"). Wenn dann U2 online ist, ist die socket.io von U2 nicht null, sodass die Nachricht gesendet wird. Wenn der Socket von U2 null ist, wird er in die Warteschlange gestellt, bis U2 online geht.
Ari Porad
1
Ich glaube @igorpavlov ist richtig. Es wird einen Zeitraum geben, in dem der Client tatsächlich getrennt wird, der Server dies jedoch nicht weiß, da der Herzschlag noch nicht aufgetreten ist. In diesem Zeitraum this.socket.iowird dies nicht der Fall sein null, und der Server wird versuchen, die Nachrichten zuzustellen.
Michelle Tilley
0

Wie bereits in einer anderen Antwort geschrieben, glaube ich auch, dass Sie die Echtzeit als Bonus betrachten sollten: Das System sollte auch ohne Echtzeit arbeiten können.

Ich entwickle einen Unternehmens-Chat für ein großes Unternehmen (iOS, Android, Web-Frontend und .net Core + PostGres-Backend) und nachdem ich eine Möglichkeit für den Websocket entwickelt habe, die Verbindung wiederherzustellen (über eine Socket-UUID) und nicht zugestellte Nachrichten zu erhalten (in einer Warteschlange gespeichert) Ich habe verstanden, dass es eine bessere Lösung gibt: Resync über die Rest-API.

Grundsätzlich habe ich Websocket nur für Echtzeit verwendet, mit einem Integer-Tag für jede Echtzeitnachricht (Benutzer online, Schreibmaschinen, Chat-Nachricht usw.) zur Überwachung verlorener Nachrichten.

Wenn der Client eine ID erhält, die nicht monolithisch ist (+1), versteht er, dass sie nicht synchron ist. Daher werden alle Socket-Nachrichten gelöscht und eine erneute Synchronisierung aller Beobachter über die REST-API angefordert.

Auf diese Weise können wir während des Offline-Zeitraums viele Variationen im Status der Anwendung verarbeiten, ohne beim erneuten Verbinden Tonnen von Websocket-Nachrichten hintereinander analysieren zu müssen, und wir sind sicher, synchronisiert zu werden (da das letzte Synchronisierungsdatum nur von der REST-API festgelegt wird , nicht aus der Steckdose).

Der einzige schwierige Teil ist die Überwachung auf Echtzeitnachrichten von dem Moment an, in dem Sie die REST-API aufrufen, bis zu dem Moment, in dem der Server antwortet, da das, was aus der Datenbank gelesen wird, Zeit benötigt, um zum Client zurückzukehren, und in der Zwischenzeit können Abweichungen auftreten, sodass sie zwischengespeichert werden müssen und berücksichtigt.

Wir gehen in ein paar Monaten in Produktion, ich hoffe, bis dahin wieder schlafen zu können :)

Il Pisanello
quelle
-2

Ich habe mir dieses Zeug zuletzt angesehen und denke, ein anderer Weg könnte besser sein.

Schauen Sie sich den Azure Service-Bus an. Fragen und Themen kümmern sich um die Offline-Zustände. Die Nachricht wartet darauf, dass der Benutzer zurückkommt, und erhält dann die Nachricht.

Ist das Ausführen einer Warteschlange kostenintensiv, aber für eine einfache Warteschlange sind es etwa 0,05 US-Dollar pro Million Operationen, sodass die Entwicklungskosten höher sind als die Arbeitsstunden, die zum Schreiben eines Warteschlangensystems erforderlich sind. https://azure.microsoft.com/en-us/pricing/details/service-bus/

Und Azure Bus hat Bibliotheken und Beispiele für PHP, C #, Xarmin, Anjular, Java Script usw.

Der Server sendet also eine Nachricht und muss sich nicht um deren Verfolgung kümmern. Der Client kann Nachrichten zum Zurücksenden verwenden, um bei Bedarf auch den Lastausgleich zu übernehmen.

Wahre Lösungen
quelle
Für mich sieht es nach Produktplatzierung aus. Jemand mag dies hilfreich finden, aber dies ist nicht einmal eine Technologie, sondern ein ganzer Service, der ebenfalls bezahlt wird.
igorpavlov
-2

Versuchen Sie diese Emit-Chat-Liste

io.on('connect', onConnect);

function onConnect(socket){

  // sending to the client
  socket.emit('hello', 'can you hear me?', 1, 2, 'abc');

  // sending to all clients except sender
  socket.broadcast.emit('broadcast', 'hello friends!');

  // sending to all clients in 'game' room except sender
  socket.to('game').emit('nice game', "let's play a game");

  // sending to all clients in 'game1' and/or in 'game2' room, except sender
  socket.to('game1').to('game2').emit('nice game', "let's play a game (too)");

  // sending to all clients in 'game' room, including sender
  io.in('game').emit('big-announcement', 'the game will start soon');

  // sending to all clients in namespace 'myNamespace', including sender
  io.of('myNamespace').emit('bigger-announcement', 'the tournament will start soon');

  // sending to individual socketid (private message)
  socket.to(<socketid>).emit('hey', 'I just met you');

  // sending with acknowledgement
  socket.emit('question', 'do you think so?', function (answer) {});

  // sending without compression
  socket.compress(false).emit('uncompressed', "that's rough");

  // sending a message that might be dropped if the client is not ready to receive messages
  socket.volatile.emit('maybe', 'do you really need it?');

  // sending to all clients on this node (when using multiple nodes)
  io.local.emit('hi', 'my lovely babies');

};

Oladimeji Ajeniya
quelle