Aplikacje Firebase działają nawet wtedy, gdy aplikacja tymczasowo utraci połączenie z siecią. W tym dokumencie opisujemy kilka narzędzi do monitorowania obecności i synchronizowania stanu lokalnego ze stanem serwera.
Zarządzanie obecnością
W aplikacjach czasu rzeczywistego często przydaje się wykrywanie, kiedy klienci łączą się 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żyć do zapisywania w bazie danych, gdy klient rozłączy się z serwerami 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ć, jeśli chodzi o zwalnianie miejsca na dane, nawet jeśli połączenie zostanie przerwane lub klient ulegnie awarii. Po rozłączeniu można wykonywać wszystkie operacje zapisu, w tym ustawianie, aktualizowanie i usuwanie.
Oto prosty przykład zapisywania danych po rozłączeniu za pomocą elementu
onDisconnect primitive:
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 onDisconnect
Gdy ustanowisz operację onDisconnect(), będzie ona działać na serwerze Firebase Realtime Database. Serwer sprawdza zabezpieczenia, aby
upewnić się, że użytkownik może wykonać żądane zdarzenie zapisu, i informuje
aplikację, jeśli jest ono nieprawidłowe. Następnie serwer
monitoruje połączenie. Jeśli w dowolnym momencie połączenie zostanie przerwane lub zostanie
aktywnie zamknięte przez klienta Realtime Database, serwer ponownie sprawdzi zabezpieczenia (aby upewnić się, że operacja jest nadal prawidłowa), a następnie wywoła
zdarzenie.
Aplikacja może użyć wywołania zwrotnego w operacji zapisu
aby upewnić się, że onDisconnect zostało prawidłowo dołączone:
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); } });
Zdarzenie onDisconnect można też anulować, wywołując .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ą przydaje się, gdy aplikacja
wie, czy jest online, czy 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 /.info/connected jako false, nie ma
gwarancji, że inny klient też odczyta false.
Obsługa opóźnień
Sygnatury czasowe serwera
Serwery Firebase Realtime Database udostępniają mechanizm wstawiania
sygnatur czasowych generowanych na serwerze jako danych. Ta funkcja w połączeniu z
onDisconnect, umożliwia łatwe i niezawodne zapisywanie czasu, w którym klient Realtime Database się rozłączył:
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);
Różnica czasu
Chociaż firebase.database.ServerValue.TIMESTAMP jest znacznie dokładniejsza i preferowana w przypadku większości operacji odczytu i zapisu, czasami może się przydać oszacowanie różnicy czasu między zegarem klienta a serwerami Firebase Realtime Database. Aby uzyskać wartość w milisekundach, którą Firebase Realtime Database klienci dodają do lokalnego czasu (czas epoki w milisekundach), aby oszacować czas serwera, możesz dołączyć wywołanie zwrotne do lokalizacji /.info/serverTimeOffset. Pamiętaj, że na dokładność tego przesunięcia może wpływać
opóźnienie sieci, dlatego przydaje się ono głównie do wykrywania
dużych (> 1 sekunda) różnic w czasie zegara.
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 do obsługi obecności
Łącząc operacje rozłączania z monitorowaniem stanu połączenia i sygnaturami czasowymi 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 Realtime Database klient jest online. Gdy klienci przechodzą do trybu online, ustawiają tę lokalizację na true , a gdy się rozłączają, ustawiają sygnaturę czasową. Ta sygnatura czasowa wskazuje, kiedy dany użytkownik był ostatnio online.
Pamiętaj, że aplikacja powinna umieścić operacje rozłączania w kolejce, zanim użytkownik zostanie oznaczony jako online, aby uniknąć sytuacji wyścigu, gdy połączenie sieciowe klienta zostanie utracone, zanim oba polecenia zostaną wysłane na serwer.
Oto prosty system obecności użytkowników:
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); } });