Abhängig von der Art der Anwendung, die Sie erstellen, ist es möglicherweise hilfreich zu erkennen, welche Ihrer Nutzer oder Geräte aktiv online sind. Dies wird auch als Erkennen der "Präsenz" bezeichnet.
Wenn Sie beispielsweise eine Anwendung (etwa ein soziales Netzwerk) erstellen oder einen Pool mit IoT-Geräten bereitstellen, können Sie mithilfe dieser Informationen eine Liste der Freunde anzeigen, die online und bereit zu chatten sind, oder Ihre IoT-Geräte nach "Zuletzt erfasst" sortieren.
Cloud Firestore unterstützt die Anwesenheitserkennung nicht nativ, aber Sie können andere Firebase-Produkte verwenden, um ein Anwesenheitssystem zu erstellen.
Lösung: Cloud Functions mit Realtime Database
Wenn Sie Cloud Firestore mit dem nativen Präsenzfeature von Firebase Realtime Database verbinden möchten, verwenden Sie Cloud Functions.
Verwenden Sie die Realtime Database, um den Verbindungsstatus zu melden, und dann Cloud Functions, um diese Daten in Cloud Firestore zu spiegeln.
Präsenz in Realtime Database verwenden
Betrachten Sie zuerst, wie ein herkömmliches Präsenzsystem in Realtime Database funktioniert.
Web
// Fetch the current user's ID from Firebase Authentication. var uid = firebase.auth().currentUser.uid; // Create a reference to this user's specific status node. // This is where we will store data about being online/offline. var userStatusDatabaseRef = firebase.database().ref('/status/' + uid); // We'll create two constants which we will write to // the Realtime database when this device is offline // or online. var isOfflineForDatabase = { state: 'offline', last_changed: firebase.database.ServerValue.TIMESTAMP, }; var isOnlineForDatabase = { state: 'online', last_changed: firebase.database.ServerValue.TIMESTAMP, }; // Create a reference to the special '.info/connected' path in // Realtime Database. This path returns `true` when connected // and `false` when disconnected. firebase.database().ref('.info/connected').on('value', function(snapshot) { // If we're not currently connected, don't do anything. if (snapshot.val() == false) { return; }; // If we are currently connected, then use the 'onDisconnect()' // method to add a set which will only trigger once this // client has disconnected by closing the app, // losing internet, or any other means. userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() { // The promise returned from .onDisconnect().set() will // resolve as soon as the server acknowledges the onDisconnect() // request, NOT once we've actually disconnected: // https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect // We can now safely set ourselves as 'online' knowing that the // server will mark us as offline once we lose connection. userStatusDatabaseRef.set(isOnlineForDatabase); }); });
Dieses Beispiel ist ein vollständiges Realtime Database-Präsenzsystem. Es verarbeitet mehrere Verbindungsabbrüche, Abstürze und so weiter.
Verbindung zu Cloud Firestore wird hergestellt
Wenn Sie eine ähnliche Lösung in Cloud Firestore implementieren möchten, verwenden Sie denselben Realtime Database-Code und dann Cloud Functions, um Realtime Database und Cloud Firestore synchron zu halten.
Fügen Sie Realtime Database zu Ihrem Projekt hinzu und schließen Sie die obige Präsenzlösung ein, falls Sie dies noch nicht getan haben.
Als Nächstes synchronisierst du den Anwesenheitsstatus mit den folgenden Methoden mit Cloud Firestore:
- Lokal im Cloud Firestore-Cache des Offline-Geräts, damit die App weiß, dass sie offline ist.
- Global, mithilfe einer Cloud Functions-Funktion, damit alle anderen Geräte, die auf Cloud Firestore zugreifen, wissen, dass dieses bestimmte Gerät offline ist.
Die in dieser Anleitung empfohlenen Funktionen können nicht in einer Client-App ausgeführt werden. Sie müssen in Cloud Functions for Firebase bereitgestellt werden und erfordern serverseitige Logik aus dem Firebase Admin SDK. Eine ausführliche Anleitung finden Sie in der Cloud Functions-Dokumentation.
Lokaler Cache von Cloud Firestore wird aktualisiert
Sehen wir uns die Änderungen an, die erforderlich sind, um das erste Problem zu beheben: die Aktualisierung des lokalen Cache von Cloud Firestore.
Web
// ... var userStatusFirestoreRef = firebase.firestore().doc('/status/' + uid); // Firestore uses a different server timestamp value, so we'll // create two more constants for Firestore state. var isOfflineForFirestore = { state: 'offline', last_changed: firebase.firestore.FieldValue.serverTimestamp(), }; var isOnlineForFirestore = { state: 'online', last_changed: firebase.firestore.FieldValue.serverTimestamp(), }; firebase.database().ref('.info/connected').on('value', function(snapshot) { if (snapshot.val() == false) { // Instead of simply returning, we'll also set Firestore's state // to 'offline'. This ensures that our Firestore cache is aware // of the switch to 'offline.' userStatusFirestoreRef.set(isOfflineForFirestore); return; }; userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() { userStatusDatabaseRef.set(isOnlineForDatabase); // We'll also add Firestore set here for when we come online. userStatusFirestoreRef.set(isOnlineForFirestore); }); });
Mit diesen Änderungen haben wir dafür gesorgt, dass der lokale Cloud Firestore-Status immer den Online-/Offline-Status des Geräts widerspiegelt. Das bedeutet, dass Sie das Dokument /status/{uid} überwachen und die Daten verwenden können, um die Benutzeroberfläche so zu ändern, dass der Verbindungsstatus angezeigt wird.
Web
userStatusFirestoreRef.onSnapshot(function(doc) { var isOnline = doc.data().state == 'online'; // ... use isOnline });
Cloud Firestore wird global aktualisiert
Obwohl unsere Anwendung die Onlinepräsenz korrekt an sich selbst meldet, ist dieser Status in anderen Cloud Firestore-Anwendungen noch nicht korrekt, da der Schreibvorgang für den „Offline“-Status nur lokal erfolgt und nicht synchronisiert wird, wenn eine Verbindung wiederhergestellt wird. Um dem entgegenzuwirken, verwenden wir eine Cloud Functions-Funktion, die den Pfad status/{uid} in der Realtime Database überwacht. Wenn sich der Wert der Realtime Database ändert, wird der Wert mit Cloud Firestore synchronisiert, sodass der Status aller Nutzer korrekt ist.
Node.js
const functions = require('firebase-functions/v1'); const { initializeApp } = require('firebase-admin/app'); const { getFirestore } = require('firebase-admin/firestore'); initializeApp(); // Since this code will be running in the Cloud Functions environment // we call initialize Firestore without any arguments because it // detects authentication from the environment. const firestore = getFirestore(); // Create a new function which is triggered on changes to /status/{uid} // Note: This is a Realtime Database trigger, *not* Firestore. exports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate( async (change, context) => { // Get the data written to Realtime Database const eventStatus = change.after.val(); // Then use other event data to create a reference to the // corresponding Firestore document. const userStatusFirestoreRef = firestore.doc(`status/${context.params.uid}`); // It is likely that the Realtime Database change that triggered // this event has already been overwritten by a fast change in // online / offline status, so we'll re-read the current data // and compare the timestamps. const statusSnapshot = await change.after.ref.once('value'); const status = statusSnapshot.val(); functions.logger.log(status, eventStatus); // If the current timestamp for this data is newer than // the data that triggered this event, we exit this function. if (status.last_changed > eventStatus.last_changed) { return null; } // Otherwise, we convert the last_changed field to a Date eventStatus.last_changed = new Date(eventStatus.last_changed); // ... and write it to Firestore. return userStatusFirestoreRef.set(eventStatus); });
Nachdem du diese Funktion bereitgestellt hast, hast du ein vollständiges Anwesenheitssystem, das mit Cloud Firestore ausgeführt wird. Im Folgenden finden Sie ein Beispiel für das Monitoring von Nutzern, die online oder offline gehen, mithilfe einer where()-Abfrage.
Web
firebase.firestore().collection('status') .where('state', '==', 'online') .onSnapshot(function(snapshot) { snapshot.docChanges().forEach(function(change) { if (change.type === 'added') { var msg = 'User ' + change.doc.id + ' is online.'; console.log(msg); // ... } if (change.type === 'removed') { var msg = 'User ' + change.doc.id + ' is offline.'; console.log(msg); // ... } }); });
Beschränkungen
Die Verwendung der Realtime Database, um Ihrer Cloud Firestore-Anwendung eine Präsenz hinzuzufügen, ist skalierbar und effektiv, hat aber einige Einschränkungen:
- Debouncing: Wenn Sie auf Echtzeitänderungen in Cloud Firestore warten, werden mit dieser Lösung wahrscheinlich mehrere Änderungen ausgelöst. Wenn diese Änderungen mehr Ereignisse auslösen, als Sie möchten, können Sie die Cloud Firestore-Ereignisse manuell entprellen.
- Konnektivität: Bei dieser Implementierung wird die Konnektivität zur Realtime Database gemessen, nicht zu Cloud Firestore. Wenn der Verbindungsstatus zu jeder Datenbank nicht derselbe ist, meldet diese Lösung möglicherweise einen falschen Präsenzstatus.
- Android: Unter Android wird die Realtime Database nach 60 Sekunden Inaktivität vom Backend getrennt. Inaktivität bedeutet, dass es keine offenen Listener oder ausstehenden Vorgänge gibt. Wir empfehlen, zusätzlich zu
.info/connectedeinen Wert-Event-Listener zu einem Pfad hinzuzufügen, um die Verbindung offen zu halten. Beispielsweise können Sie zu Beginn jeder SitzungFirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced()verwenden. Weitere Informationen finden Sie unter Verbindungsstatus ermitteln.