Bauen Sie Präsenz in Cloud Firestore auf

Abhängig von der Art der App, die Sie erstellen, ist es möglicherweise hilfreich zu erkennen, welche Ihrer Benutzer oder Geräte aktiv online sind – auch bekannt als „Anwesenheitserkennung“.

Wenn Sie beispielsweise eine App wie ein soziales Netzwerk erstellen oder eine Flotte von IoT-Geräten bereitstellen, können Sie diese Informationen verwenden, um eine Liste mit Freunden anzuzeigen, die online sind und frei chatten können, oder Ihre IoT-Geräte nach „zuletzt gesehen“ sortieren ."

Cloud Firestore unterstützt Präsenz nicht nativ, aber Sie können andere Firebase-Produkte nutzen, um ein Präsenzsystem zu erstellen.

Lösung: Cloud-Funktionen mit Echtzeitdatenbank

Verwenden Sie Cloud Functions, um Cloud Firestore mit der nativen Präsenzfunktion von Firebase Realtime Database zu verbinden.

Verwenden Sie Realtime Database, um den Verbindungsstatus zu melden, und verwenden Sie dann Cloud Functions, um diese Daten in Cloud Firestore zu spiegeln.

Anwesenheit in der Echtzeitdatenbank verwenden

Überlegen Sie zunächst, wie ein herkömmliches Anwesenheitssystem in Realtime Database funktioniert.

Netz

// 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 Echtzeit-Datenbank-Präsenzsystem. Es behandelt mehrere Verbindungsabbrüche, Abstürze und so weiter.

Verbindung zu Cloud Firestore herstellen

Um eine ähnliche Lösung in Cloud Firestore zu implementieren, verwenden Sie denselben Realtime Database-Code und verwenden Sie dann Cloud Functions, um Realtime Database und Cloud Firestore synchron zu halten.

Wenn Sie dies noch nicht getan haben, fügen Sie Ihrem Projekt Realtime Database hinzu und binden Sie die obige Präsenzlösung ein.

Als Nächstes synchronisieren Sie den Anwesenheitsstatus mithilfe der folgenden Methoden mit Cloud Firestore:

  1. Lokal in den Cloud Firestore-Cache des Offline-Geräts, damit die App weiß, dass sie offline ist.
  2. Global, Verwendung einer Cloud-Funktion, damit alle anderen Geräte, die auf Cloud Firestore zugreifen, wissen, dass dieses bestimmte Gerät offline ist.

Lokalen Cache von Cloud Firestore aktualisieren

Werfen wir einen Blick auf die Änderungen, die erforderlich sind, um das erste Problem zu erfüllen – das Aktualisieren des lokalen Caches von Cloud Firestore.

Netz

// ...
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 nun sichergestellt, dass der lokale Cloud Firestore-Status immer den Online-/Offline-Status des Geräts widerspiegelt. Das bedeutet, dass Sie das /status/{uid} -Dokument anhören und die Daten verwenden können, um Ihre Benutzeroberfläche so zu ändern, dass sie den Verbindungsstatus widerspiegelt.

Netz

userStatusFirestoreRef.onSnapshot(function(doc) {
    var isOnline = doc.data().state == 'online';
    // ... use isOnline
});

Cloud Firestore global aktualisieren

Obwohl unsere Anwendung die Online-Präsenz korrekt an sich selbst meldet, ist dieser Status in anderen Cloud Firestore-Apps noch nicht korrekt, da unser „Offline“-Statusschreibvorgang nur lokal ist und nicht synchronisiert wird, wenn eine Verbindung wiederhergestellt wird. Um dem entgegenzuwirken, verwenden wir eine Cloud-Funktion, die den status/{uid} -Pfad in der Echtzeitdatenbank überwacht. Wenn sich der Wert der Echtzeitdatenbank ändert, wird der Wert mit Cloud Firestore synchronisiert, sodass der Status aller Benutzer korrekt ist.

Node.js

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.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 = admin.firestore();

// 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);
    });

Sobald Sie diese Funktion bereitgestellt haben, verfügen Sie über ein vollständiges Anwesenheitssystem, das mit Cloud Firestore ausgeführt wird. Nachfolgend finden Sie ein Beispiel für die Überwachung aller Benutzer, die mit einer where() Abfrage online gehen oder offline gehen.

Netz

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);
                // ...
            }
        });
    });

Einschränkungen

Die Verwendung von Realtime Database zum Hinzufügen von Präsenz zu Ihrer Cloud Firestore-App ist skalierbar und effektiv, weist jedoch einige Einschränkungen auf:

  • Entprellung – Beim Abhören von Echtzeitänderungen in Cloud Firestore löst diese Lösung wahrscheinlich mehrere Änderungen aus. Wenn diese Änderungen mehr Ereignisse auslösen als gewünscht, entprellen Sie die Cloud Firestore-Ereignisse manuell.
  • Konnektivität – Diese Implementierung misst die Konnektivität zur Realtime Database, nicht zu Cloud Firestore. Wenn der Verbindungsstatus zu jeder Datenbank nicht gleich ist, meldet diese Lösung möglicherweise einen falschen Anwesenheitsstatus.
  • Android - Unter Android trennt die Echtzeitdatenbank die Verbindung zum Backend nach 60 Sekunden Inaktivität. Inaktivität bedeutet keine offenen Listener oder ausstehenden Operationen. Um die Verbindung offen zu halten, empfehlen wir Ihnen, einem Pfad neben .info/connected einen Wertereignis-Listener hinzuzufügen. Beispielsweise könnten Sie FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() zu Beginn jeder Sitzung ausführen. Weitere Informationen finden Sie unter Verbindungsstatus ermitteln .