Firebase-Anwendungen funktionieren auch dann, wenn Ihre App vorübergehend die Netzwerkverbindung verliert. Wir stellen mehrere Tools zur Überwachung der Anwesenheit und zur Synchronisierung des lokalen Status mit dem Serverstatus bereit, die in diesem Dokument vorgestellt werden.
Präsenz verwalten
In Echtzeitanwendungen ist es oft nützlich zu erkennen, wann Clients eine Verbindung herstellen und trennen. Beispielsweise möchten Sie möglicherweise einen Benutzer als „offline“ markieren, wenn sein Client die Verbindung trennt.
Firebase-Datenbank-Clients stellen einfache Grundelemente bereit, die Sie zum Schreiben in die Datenbank verwenden können, wenn ein Client die Verbindung zu den Firebase-Datenbankservern trennt. Diese Aktualisierungen erfolgen unabhängig davon, ob der Client die Verbindung ordnungsgemäß trennt oder nicht. Sie können sich also darauf verlassen, dass sie die Daten bereinigen, selbst wenn eine Verbindung unterbrochen wird oder ein Client abstürzt. Alle Schreibvorgänge, einschließlich Einstellen, Aktualisieren und Entfernen, können bei einer Trennung ausgeführt werden.
Hier ist ein einfaches Beispiel für das Schreiben von Daten beim Trennen der Verbindung mithilfe des onDisconnect
Primitivs:
Web modular API
import { getDatabase, ref, onDisconnect } from "firebase/database"; const db = getDatabase(); const presenceRef = ref(db, "disconnectmessage"); // Write a string when this client loses connection onDisconnect(presenceRef).set("I disconnected!");
Web namespaced API
var presenceRef = firebase.database().ref("disconnectmessage"); // Write a string when this client loses connection presenceRef.onDisconnect().set("I disconnected!");
So funktioniert onDisconnect
Wenn Sie einen onDisconnect()
Vorgang einrichten, wird der Vorgang auf dem Firebase Realtime Database-Server gespeichert. Der Server überprüft die Sicherheit, um sicherzustellen, dass der Benutzer das angeforderte Schreibereignis ausführen kann, und informiert Ihre App, wenn es ungültig ist. Der Server überwacht dann die Verbindung. Wenn die Verbindung zu irgendeinem Zeitpunkt abläuft oder vom Realtime Database-Client aktiv geschlossen wird, überprüft der Server die Sicherheit ein zweites Mal (um sicherzustellen, dass der Vorgang noch gültig ist) und ruft dann das Ereignis auf.
Ihre App kann den Rückruf beim Schreibvorgang verwenden, um sicherzustellen, dass onDisconnect
korrekt angehängt wurde:
Web modular API
onDisconnect(presenceRef).remove().catch((err) => { if (err) { console.error("could not establish onDisconnect event", err); } });
Web namespaced API
presenceRef.onDisconnect().remove((err) => { if (err) { console.error("could not establish onDisconnect event", err); } });
Ein onDisconnect
Ereignis kann auch durch Aufrufen .cancel()
abgebrochen werden:
Web modular API
const onDisconnectRef = onDisconnect(presenceRef); onDisconnectRef.set("I disconnected"); // some time later when we change our minds onDisconnectRef.cancel();
Web namespaced API
var onDisconnectRef = presenceRef.onDisconnect(); onDisconnectRef.set("I disconnected"); // some time later when we change our minds onDisconnectRef.cancel();
Verbindungsstatus erkennen
Für viele anwesenheitsbezogene Funktionen ist es hilfreich, dass Ihre App weiß, wann sie online oder offline ist. Firebase Realtime Database bietet einen speziellen Speicherort unter /.info/connected
, der jedes Mal aktualisiert wird, wenn sich der Verbindungsstatus des Firebase Realtime Database-Clients ändert. Hier ist ein Beispiel:
Web modular API
import { getDatabase, ref, onValue } from "firebase/database"; const db = getDatabase(); const connectedRef = ref(db, ".info/connected"); onValue(connectedRef, (snap) => { if (snap.val() === true) { console.log("connected"); } else { console.log("not connected"); } });
Web namespaced API
var connectedRef = firebase.database().ref(".info/connected"); connectedRef.on("value", (snap) => { if (snap.val() === true) { console.log("connected"); } else { console.log("not connected"); } });
/.info/connected
ist ein boolescher Wert, der nicht zwischen Echtzeitdatenbank-Clients synchronisiert wird, da der Wert vom Status des Clients abhängt. Mit anderen Worten: Wenn ein Client /.info/connected
als „false“ liest, ist dies keine Garantie dafür, dass ein separater Client auch „false“ liest.
Umgang mit Latenz
Server-Zeitstempel
Die Firebase-Echtzeitdatenbankserver bieten einen Mechanismus zum Einfügen von auf dem Server generierten Zeitstempeln als Daten. Diese Funktion bietet in Kombination mit onDisconnect
eine einfache Möglichkeit, zuverlässig den Zeitpunkt zu notieren, zu dem ein Realtime Database-Client die Verbindung getrennt hat:
Web modular API
import { getDatabase, ref, onDisconnect, serverTimestamp } from "firebase/database"; const db = getDatabase(); const userLastOnlineRef = ref(db, "users/joe/lastOnline"); onDisconnect(userLastOnlineRef).set(serverTimestamp());
Web namespaced API
var userLastOnlineRef = firebase.database().ref("users/joe/lastOnline"); userLastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);
Uhrversatz
Während firebase.database.ServerValue.TIMESTAMP
viel genauer und für die meisten Lese-/Schreibvorgänge vorzuziehen ist, kann es gelegentlich nützlich sein, den Zeitversatz des Clients im Vergleich zu den Servern der Firebase Realtime Database abzuschätzen. Sie können einen Rückruf an den Speicherort /.info/serverTimeOffset
anhängen, um den Wert in Millisekunden zu erhalten, den Firebase Realtime Database-Clients zur lokal gemeldeten Zeit (Epochenzeit in Millisekunden) hinzufügen, um die Serverzeit zu schätzen. Beachten Sie, dass die Genauigkeit dieses Offsets durch die Netzwerklatenz beeinträchtigt werden kann und daher vor allem zum Erkennen großer (> 1 Sekunde) Abweichungen in der Uhrzeit nützlich ist.
Web modular API
import { getDatabase, ref, onValue } from "firebase/database"; const db = getDatabase(); const offsetRef = ref(db, ".info/serverTimeOffset"); onValue(offsetRef, (snap) => { const offset = snap.val(); const estimatedServerTimeMs = new Date().getTime() + offset; });
Web namespaced API
var offsetRef = firebase.database().ref(".info/serverTimeOffset"); offsetRef.on("value", (snap) => { var offset = snap.val(); var estimatedServerTimeMs = new Date().getTime() + offset; });
Beispiel-Präsenz-App
Durch die Kombination von Trennvorgängen mit der Überwachung des Verbindungsstatus und Server-Zeitstempeln können Sie ein Benutzerpräsenzsystem aufbauen. In diesem System speichert jeder Benutzer Daten an einem Datenbankstandort, um anzuzeigen, ob ein Echtzeitdatenbank-Client online ist oder nicht. Clients setzen diesen Standort auf „true“, wenn sie online gehen, und auf einen Zeitstempel, wenn sie die Verbindung trennen. Dieser Zeitstempel gibt an, wann der angegebene Benutzer das letzte Mal online war.
Beachten Sie, dass Ihre App die Trennvorgänge in die Warteschlange stellen sollte, bevor ein Benutzer als online markiert wird, um Race Conditions für den Fall zu vermeiden, dass die Netzwerkverbindung des Clients unterbrochen wird, bevor beide Befehle an den Server gesendet werden können.
Hier ist ein einfaches Benutzerpräsenzsystem:
Web modular API
import { getDatabase, ref, onValue, push, onDisconnect, set, serverTimestamp } from "firebase/database"; // Since I can connect from multiple devices or browser tabs, we store each connection instance separately // any time that connectionsRef's value is null (i.e. has no children) I am offline const db = getDatabase(); const myConnectionsRef = ref(db, 'users/joe/connections'); // stores the timestamp of my last disconnect (the last time I was seen online) const lastOnlineRef = ref(db, 'users/joe/lastOnline'); const connectedRef = ref(db, '.info/connected'); onValue(connectedRef, (snap) => { if (snap.val() === true) { // We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect) const con = push(myConnectionsRef); // When I disconnect, remove this device onDisconnect(con).remove(); // Add this device to my connections list // this value could contain info about the device or a timestamp too set(con, true); // When I disconnect, update the last time I was seen online onDisconnect(lastOnlineRef).set(serverTimestamp()); } });
Web namespaced API
// Since I can connect from multiple devices or browser tabs, we store each connection instance separately // any time that connectionsRef's value is null (i.e. has no children) I am offline var myConnectionsRef = firebase.database().ref('users/joe/connections'); // stores the timestamp of my last disconnect (the last time I was seen online) var lastOnlineRef = firebase.database().ref('users/joe/lastOnline'); var connectedRef = firebase.database().ref('.info/connected'); connectedRef.on('value', (snap) => { if (snap.val() === true) { // We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect) var con = myConnectionsRef.push(); // When I disconnect, remove this device con.onDisconnect().remove(); // Add this device to my connections list // this value could contain info about the device or a timestamp too con.set(true); // When I disconnect, update the last time I was seen online lastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP); } });