Präsenz in Cloud Firestore aufbauen

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 als „Anwesenheitserkennung“ bezeichnet.

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 von Freunden anzuzeigen, die online sind und frei chatten können, oder Ihre IoT-Geräte nach "Zuletzt gesehen" sortieren ."

Cloud Firestore unterstützt die 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 Anwesenheitsfunktion 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.

Präsenz in Echtzeitdatenbank verwenden

Betrachten Sie zunächst, wie ein herkömmliches Anwesenheitssystem in der Echtzeitdatenbank 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 komplettes 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 nicht bereits haben, fügen Sie Realtime - Datenbank zu einem Projekt und schließen die oben Präsenz Lösung.

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 mit einer Cloud Functions-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

Sehen wir uns die Änderungen an, die erforderlich sind, um das erste Problem zu lösen – die Aktualisierung 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 sichergestellt wir jetzt , dass der lokale Wolke Firestor Zustand wird immer die Online- / Offline - Status des Geräts anzupassen. Das heißt , Sie auf das hören /status/{uid} Dokument und die Daten verwenden , um Ihre UI zu ändern Verbindungsstatus zu reflektieren.

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 Schreibstatus für den Offline-Status nur lokal ist und nicht synchronisiert wird, wenn eine Verbindung wiederhergestellt wird. Um dem entgegenzuwirken, werden wir eine Cloud - Funktion verwenden , die die Uhren status/{uid} Pfad in Echtzeit - Datenbank. 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 bereitstellen, verfügen Sie über ein vollständiges Anwesenheitssystem, das mit Cloud Firestore ausgeführt wird. Unten ist ein Beispiel für alle Benutzer zu überwachen , die online sind , oder offline gehen mit where() Abfrage.

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 - wenn sie auf Echtzeitänderungen in Cloud - Firestor hören, ist diese Lösung wahrscheinlich mehrere Veränderungen auszulösen. Wenn diese Änderungen mehr Ereignisse auslösen, als Sie möchten, debouncen Sie die Cloud Firestore-Ereignisse manuell.
  • Connectivity - diese Implementierung Maßnahmen Anbindung an Realtime - Datenbank, nicht zu Wolke Firestor. Wenn der Verbindungsstatus zu jeder Datenbank nicht gleich ist, kann diese Lösung einen falschen Anwesenheitsstatus melden.
  • Android - auf Android, die Echtzeit - Datenbank trennt vom Back - End nach 60 Sekunden Inaktivität. Inaktivität bedeutet keine offenen Listener oder ausstehenden Operationen. Um die Verbindung offen zu halten, wir empfehlen Ihnen , einen Wert Event - Listener auf einen Pfad hinzufügen neben .info/connected . Zum Beispiel könnten Sie tun FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() zu Beginn jeder Sitzung. Weitere Informationen finden Sie Detecting Connection State .