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

Lösung: Cloud Functions mit Realtime Database

Wenn Sie Cloud Firestore mit der nativen Anwesenheitsfunktion der Firebase Realtime Database verbinden möchten, verwenden Sie Cloud Functions.

Verwenden Sie Realtime Database, um den Verbindungsstatus zu melden, und 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 zu synchronisieren.

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 Anwesenheitsstatus mit Cloud Firestore. Verwenden Sie dazu die folgenden Methoden:

  1. Lokal in den Cloud Firestore-Cache des Offlinegeräts, damit der App mitgeteilt wird, dass sie offline ist
  2. Global mithilfe von Cloud Functions, damit allen anderen Geräten, die auf Cloud Firestore zugreifen, mitgeteilt wird, dass dieses Gerät offline ist.

Die in diesem Tutorial 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 zur Erfüllung des ersten Problems erforderlich sind: das Aktualisieren 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);
    });
});

Durch diese Änderungen haben wir dafür gesorgt, dass der lokale Cloud Firestore-Status immer den Online-/Offlinestatus des Geräts widerspiegelt. 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 Onlinepräsenz korrekt an sich selbst meldet, ist dieser Status in anderen Cloud Firestore-Apps noch nicht korrekt, da unser „Offline“-Status nur lokal geschrieben und nicht synchronisiert wird, wenn eine Verbindung wiederhergestellt wird. Um dem entgegenzuwirken, verwenden wir eine Cloud-Funktion, die den Pfad status/{uid} in der Realtime Database überwacht. Wenn sich der Wert in der Realtime Database ändert, wird der Wert mit Cloud Firestore synchronisiert, damit 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, haben Sie ein vollständiges Anwesenheitssystem, das mit Cloud Firestore ausgeführt wird. Unten sehen 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 von Realtime Database zum Hinzufügen von Anwesenheit zu Ihrer Cloud Firestore App ist skalierbar und effektiv, hat aber einige Einschränkungen:

  • Entprellen: Wenn Sie auf Echtzeitänderungen in Cloud Firestore achten, löst diese Lösung wahrscheinlich mehrere Änderungen aus. Wenn diese Änderungen mehr Ereignisse als gewünscht auslösen, müssen 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 Verbindung zur Realtime Database nach 60 Sekunden Inaktivität getrennt. 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.