Le applicazioni Firebase funzionano anche se l'app perde temporaneamente la connessione di rete. In questo documento vengono presentati diversi strumenti per monitorare la presenza e sincronizzare lo stato locale con lo stato del server.
Gestione della presenza
Nelle applicazioni in tempo reale è spesso utile rilevare quando i client si connettono e si disconnettono. Ad esempio, potresti voler contrassegnare un utente come "offline" quando il suo client si disconnette.
I client Firebase Database forniscono primitive semplici che puoi utilizzare per scrivere nel database quando un client si disconnette dai server di Firebase Database. Questi aggiornamenti vengono eseguiti indipendentemente dal fatto che il client si disconnetta correttamente o meno, in modo da poter fare affidamento su di essi per pulire i dati anche se una connessione viene interrotta o un client si arresta in modo anomalo. Tutte le operazioni di scrittura, inclusi impostazione, aggiornamento e rimozione, possono essere eseguite in caso di disconnessione.
Ecco un semplice esempio di scrittura dei dati al momento della disconnessione utilizzando la primitiva
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!");
Come funziona onDisconnect
Quando stabilisci un'operazione onDisconnect()
, l'operazione
risiede sul server Firebase Realtime Database. Il server controlla la sicurezza per
assicurarsi che l'utente possa eseguire l'evento di scrittura richiesto e comunica
alla tua app se non è valido. Il server monitora
la connessione. Se la connessione scade in qualsiasi momento o viene
chiusa attivamente dal client Realtime Database, il server controlla la sicurezza una
seconda volta (per assicurarsi che l'operazione sia ancora valida) e poi richiama
l'evento.
La tua app può utilizzare il callback sull'operazione di scrittura
per assicurarsi che onDisconnect
sia stato allegato correttamente:
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); } });
Un evento onDisconnect
può essere annullato anche chiamando il numero .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();
Rilevamento dello stato della connessione
Per molte funzionalità correlate alla presenza, è utile che l'app
sappia quando è online o offline. Firebase Realtime Database
fornisce una posizione speciale in /.info/connected
che
viene aggiornata ogni volta che cambia lo stato della connessione del client Firebase Realtime Database. Ecco un esempio:
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
è un valore booleano che non viene
sincronizzato tra i client Realtime Database perché il valore
dipende dallo stato del client. In altre parole, se un client
legge /.info/connected
come false, non è
garantito che un client separato leggerà anche false.
Latenza di gestione
Timestamp del server
I server Firebase Realtime Database forniscono un meccanismo per inserire
i timestamp generati sul server come dati. Questa funzionalità, combinata con
onDisconnect
, offre un modo semplice per annotare in modo affidabile
l'ora in cui un client Realtime Database si è disconnesso:
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);
Skew dell'orologio
Sebbene firebase.database.ServerValue.TIMESTAMP
sia molto più
accurato e preferibile per la maggior parte delle operazioni di lettura/scrittura,
a volte può essere utile stimare la differenza di orologio del client rispetto
ai server di Firebase Realtime Database. Puoi
collegare un callback alla posizione /.info/serverTimeOffset
per ottenere il valore, in millisecondi, che i client Firebase Realtime Database
aggiungono all'ora locale segnalata (ora Unix in millisecondi) per stimare
l'ora del server. Tieni presente che l'accuratezza di questo offset può essere influenzata dalla latenza di rete, pertanto è utile principalmente per rilevare discrepanze di grandi dimensioni (> 1 secondo) nell'ora dell'orologio.
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; });
App di esempio per la presenza
Combinando le operazioni di disconnessione con il monitoraggio dello stato della connessione e i timestamp del server, puoi creare un sistema di presenza degli utenti. In questo sistema, ogni utente memorizza i dati in una posizione del database per indicare se un client Realtime Database è online. I client impostano questa posizione su true quando si connettono a internet e un timestamp quando si disconnettono. Questo timestamp indica l'ultima volta che l'utente specificato è stato online.
Tieni presente che la tua app deve mettere in coda le operazioni di disconnessione prima che un utente venga contrassegnato come online, per evitare condizioni di competizione nel caso in cui la connessione di rete del client venga persa prima che entrambi i comandi possano essere inviati al server.
Ecco un semplice sistema di presenza dell'utente:
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); } });