Präsenz in Cloud Firestore aufbauen

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 Präsenzerkennung nicht nativ. Sie können aber andere Firebase-Produkte nutzen, um ein Präsenzsystem zu erstellen.

Lösung: Cloud Functions mit Realtime Database

Cloud Firestore mit der nativen Firebase Realtime Database verbinden nutzen Sie Cloud Functions.

Verwenden Sie Realtime Database, um den Verbindungsstatus abzurufen, und verwenden Sie 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

Um eine ähnliche Lösung in Cloud Firestore zu implementieren, verwenden Sie dasselbe Realtime Database-Code und nutzen Sie Cloud Functions, um Realtime Database und Cloud Firestore synchronisiert.

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 synchronisieren Sie den Präsenzstatus mithilfe der folgenden Methoden mit Cloud Firestore:

  1. Lokal im Cloud Firestore-Cache des Offlinegeräts, damit der Anwendung mitgeteilt wird, dass sie offline ist.
  2. Global mithilfe einer Cloud Functions-Funktion, sodass alle anderen Geräte, die auf Cloud Firestore weiß, dass dieses Gerät offline ist.

Lokaler Cache von Cloud Firestore wird aktualisiert

Sehen wir uns die Änderungen an, die zur Lösung der ersten Anforderung erforderlich sind: das Aktualisieren des lokalen Caches 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 nun dafür gesorgt, dass der lokale Cloud Firestore-Status immer den Online-/Offlinestatus des Geräts widerspiegeln. Das bedeutet, dass Sie das Dokument /status/{uid} überwachen und die Daten verwenden können, um Ihre UI entsprechend dem Verbindungsstatus zu ändern.

Web

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

Cloud Firestore wird global aktualisiert

Obwohl unsere Anwendung die Online-Präsenz korrekt an sich selbst meldet, erscheint dieser Status werden in anderen Cloud Firestore-Apps noch nicht genau sein, weil unser "Offline" Der Schreibvorgangsstatus ist nur lokal und wird nicht synchronisiert, wenn die Verbindung wiederhergestellt ist. Gegenangebot Dafür verwenden wir eine Cloud Functions-Funktion, die den status/{uid}-Pfad in Echtzeit überwacht. Datenbank. Wenn sich der Realtime Database-Wert ändert, wird er mit Cloud Firestore synchronisiert, sodass die Status aller Nutzer korrekt sind.

Node.js

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

Nachdem Sie diese Funktion bereitgestellt haben, wird ein vollständiges Anwesenheitssystem ausgeführt. mit Cloud Firestore. Im Folgenden finden Sie ein Beispiel für das Monitoring von Nutzern, die mit einer where()-Abfrage online oder offline gehen.

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 von Realtime Database zum Hinzufügen von Präsenz zu Ihrer Cloud Firestore-Anwendung ist skalierbar und effektiv, hat jedoch einige Einschränkungen:

  • Entprellung: Wenn Sie Echtzeitänderungen in Cloud Firestore überwachen, werden mit dieser Lösung wahrscheinlich mehrere Änderungen ausgelöst. Wenn diese Änderungen mehr Ereignisse als gewünscht auslösen, müssen Sie die Cloud Firestore-Ereignisse manuell entprellen.
  • Konnektivität: Mit dieser Implementierung wird die Konnektivität zu Realtime Database und nicht zu Cloud Firestore gemessen. Wenn der Verbindungsstatus zu jeder Datenbank nicht derselbe ist, meldet diese Lösung möglicherweise einen falschen Präsenzstatus.
  • Android: Unter Android trennt Realtime Database die Verbindung zum Back-End nach 60 Sekunden Inaktivität. Inaktivität bedeutet, dass es keine offenen Listener oder ausstehenden Vorgänge gibt. Wir empfehlen, zusätzlich zu .info/connected einen Wert-Event-Listener zu einem Pfad hinzuzufügen, um die Verbindung offen zu halten. Beispielsweise können Sie zu Beginn jeder Sitzung FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() verwenden. Weitere Informationen finden Sie unter Verbindungsstatus ermitteln.