Aplikacje Firebase działają nawet wtedy, gdy aplikacja tymczasowo utraci połączenie z siecią. Udostępniamy kilka narzędzi do monitorowania obecności i synchronizowania stanu lokalnego ze stanem serwera, które opisujemy w tym dokumencie.
Zarządzanie obecnością
W aplikacjach działających w czasie rzeczywistym często przydaje się wykrywanie, kiedy klienci się łączą i rozłączają. Możesz na przykład oznaczyć użytkownika jako „offline”, gdy jego klient się rozłączy.
Klienci Bazy danych Firebase udostępniają proste elementy, których możesz używać do zapisywania danych w bazie, gdy klient odłączy się od serwerów Bazy danych Firebase. Te aktualizacje są wykonywane niezależnie od tego, czy klient rozłączy się prawidłowo, czy nie. Możesz więc na nich polegać przy czyszczeniu danych, nawet jeśli połączenie zostanie przerwane lub klient ulegnie awarii. Wszystkie operacje zapisu, w tym ustawianie, aktualizowanie i usuwanie, można wykonywać po odłączeniu.
Oto prosty przykład zapisywania danych po odłączeniu za pomocą elementu onDisconnect
:
Web
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
var presenceRef = firebase.database().ref("disconnectmessage"); // Write a string when this client loses connection presenceRef.onDisconnect().set("I disconnected!");
Jak działa funkcja onDisconnect
Gdy utworzysz operację onDisconnect()
, będzie ona przechowywana na serwerze Firebase Realtime Database. Serwer sprawdza zabezpieczenia, aby upewnić się, że użytkownik może wykonać żądane zdarzenie zapisu, i informuje aplikację, czy jest ono nieprawidłowe. Serwer monitoruje połączenie. Jeśli w dowolnym momencie połączenie zostanie przerwane lub aktywnie zamknięte przez klienta Realtime Database, serwer po raz drugi sprawdzi bezpieczeństwo (aby upewnić się, że operacja jest nadal ważna), a następnie wywoła zdarzenie.
Aplikacja może używać wywołania zwrotnego w operacji zapisu, aby upewnić się, że onDisconnect
został prawidłowo dołączony:
Web
onDisconnect(presenceRef).remove().catch((err) => { if (err) { console.error("could not establish onDisconnect event", err); } });
Web
presenceRef.onDisconnect().remove((err) => { if (err) { console.error("could not establish onDisconnect event", err); } });
onDisconnect
Wydarzenie można też anulować, dzwoniąc pod numer .cancel()
:
Web
const onDisconnectRef = onDisconnect(presenceRef); onDisconnectRef.set("I disconnected"); // some time later when we change our minds onDisconnectRef.cancel();
Web
var onDisconnectRef = presenceRef.onDisconnect(); onDisconnectRef.set("I disconnected"); // some time later when we change our minds onDisconnectRef.cancel();
Wykrywanie stanu połączenia
W przypadku wielu funkcji związanych z obecnością przydatne jest, aby aplikacja wiedziała, kiedy jest online lub offline. Firebase Realtime Database
udostępnia specjalną lokalizację /.info/connected
, która
jest aktualizowana za każdym razem, gdy zmienia się stan połączenia klienta Firebase Realtime Database. Oto przykład:
Web
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
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
to wartość logiczna, która nie jest synchronizowana między klientami Realtime Database, ponieważ zależy od stanu klienta. Innymi słowy, jeśli jeden klient odczyta wartość /.info/connected
jako fałsz, nie ma gwarancji, że inny klient również odczyta wartość fałsz.
Opóźnienie obsługi
Sygnatury czasowe serwera
Serwery Firebase Realtime Database umożliwiają wstawianie sygnatur czasowych generowanych na serwerze jako danych. Ta funkcja w połączeniu z onDisconnect
umożliwia łatwe i wiarygodne zapisywanie czasu, w którym klient Realtime Database rozłączył się:
Web
import { getDatabase, ref, onDisconnect, serverTimestamp } from "firebase/database"; const db = getDatabase(); const userLastOnlineRef = ref(db, "users/joe/lastOnline"); onDisconnect(userLastOnlineRef).set(serverTimestamp());
Web
var userLastOnlineRef = firebase.database().ref("users/joe/lastOnline"); userLastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);
Clock Skew
Chociaż firebase.database.ServerValue.TIMESTAMP
jest znacznie dokładniejszy i preferowany w przypadku większości operacji odczytu i zapisu, czasami może być przydatne oszacowanie odchylenia zegara klienta względem serwerów Firebase Realtime Database. Możesz dołączyć wywołanie zwrotne do lokalizacji /.info/serverTimeOffset
, aby uzyskać wartość w milisekundach, którą Firebase Realtime DatabaseklientyFirebase Realtime Database dodają do lokalnego zgłoszonego czasu (czas epoki w milisekundach), aby oszacować czas serwera. Pamiętaj, że na dokładność tego przesunięcia może wpływać opóźnienie sieci, dlatego jest ono przydatne głównie do wykrywania dużych (powyżej 1 sekundy) rozbieżności w czasie zegarowym.
Web
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
var offsetRef = firebase.database().ref(".info/serverTimeOffset"); offsetRef.on("value", (snap) => { var offset = snap.val(); var estimatedServerTimeMs = new Date().getTime() + offset; });
Przykładowa aplikacja Presence
Łącząc operacje rozłączania z monitorowaniem stanu połączenia i znacznikami czasu serwera, możesz utworzyć system obecności użytkowników. W tym systemie każdy użytkownik przechowuje dane w lokalizacji bazy danych, aby wskazać, czy klient Realtime Database jest online. Klienci ustawiają tę lokalizację na wartość „prawda”, gdy są online, a znacznik czasu, gdy się rozłączają. Ta sygnatura czasowa wskazuje, kiedy dany użytkownik był ostatnio online.
Pamiętaj, że aplikacja powinna umieszczać operacje odłączania w kolejce, zanim użytkownik zostanie oznaczony jako online. Pozwoli to uniknąć sytuacji wyścigu w przypadku utraty połączenia sieciowego klienta, zanim oba polecenia zostaną wysłane na serwer.
Oto prosty system obecności użytkownika:
Web
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
// 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); } });