W zależności od typu tworzonej aplikacji może się przydać wykrywanie, którzy użytkownicy lub urządzenia są aktywnie online – czyli wykrywanie „obecności”.
Jeśli na przykład tworzysz aplikację taką jak sieć społecznościowa lub wdrażasz flotę urządzeń IoT, możesz użyć tych informacji, aby wyświetlić listę znajomych, którzy są online i mogą rozmawiać, lub posortować urządzenia IoT według daty ostatniej aktywności.
Cloud Firestore nie obsługuje natywnie obecności, ale możesz wykorzystać inne usługi Firebase, aby utworzyć system obecności.
Rozwiązanie: Cloud Functions z Bazą danych czasu rzeczywistego
Aby połączyć Cloud Firestore z natywną funkcją obecności Bazy danych czasu rzeczywistego Firebase, użyj Cloud Functions.
Użyj Bazy danych czasu rzeczywistego, aby zgłaszać stan połączenia, a następnie użyj Cloud Functions, aby odzwierciedlać te dane w Cloud Firestore.
Korzystanie z obecności w Bazie danych czasu rzeczywistego
Najpierw zastanów się, jak działa tradycyjny system obecności w Bazie danych czasu rzeczywistego.
Sieć
// 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); }); });
Ten przykład to kompletny system obecności w Bazie danych czasu rzeczywistego. Obsługuje on wiele rozłączeń, awarii itp.
Łączenie z Cloud Firestore
Aby wdrożyć podobne rozwiązanie w Cloud Firestore, użyj tego samego kodu Bazy danych czasu rzeczywistego, a następnie użyj Cloud Functions, aby synchronizować Bazę danych czasu rzeczywistego i Cloud Firestore.
Jeśli jeszcze tego nie zrobisz, dodaj Bazę danych czasu rzeczywistego do projektu i uwzględnij powyższe rozwiązanie dotyczące obecności.
Następnie zsynchronizujesz stan obecności z Cloud Firestore za pomocą tych metod:
- Lokalnie, w pamięci podręcznej Cloud Firestore urządzenia offline, aby aplikacja wiedziała, że jest offline.
- Globalnie, za pomocą Cloud Functions, aby wszystkie inne urządzenia uzyskujące dostęp do Cloud Firestore wiedziały, że to konkretne urządzenie jest offline.
Funkcje zalecane w tym samouczku nie mogą być uruchamiane w aplikacji klienckiej. Muszą być wdrożone w Cloud Functions for Firebase, i wymagają logiki po stronie serwera z pakietu Firebase Admin SDK. Szczegółowe wskazówki znajdziesz w dokumentacji Cloud Functions.
Aktualizowanie Cloud Firestore's pamięci podręcznej lokalnej
Przyjrzyjmy się zmianom wymaganym do rozwiązania pierwszego problemu – aktualizowania Cloud Firestore pamięci podręcznej lokalnej.
Sieć
// ... 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); }); });
Dzięki tym zmianom mamy pewność, że lokalny Cloud Firestore stan będzie zawsze
odzwierciedlać stan online/offline urządzenia. Oznacza to, że możesz nasłuchiwać dokument /status/{uid} i używać danych do zmiany interfejsu, aby odzwierciedlał stan połączenia.
Sieć
userStatusFirestoreRef.onSnapshot(function(doc) { var isOnline = doc.data().state == 'online'; // ... use isOnline });
Aktualizowanie Cloud Firestore globalnie
Chociaż nasza aplikacja prawidłowo zgłasza obecność online, ten stan
nie będzie jeszcze dokładny w innych Cloud Firestore aplikacjach, ponieważ zapis stanu „offline”
jest tylko lokalny i nie zostanie zsynchronizowany po przywróceniu połączenia. Aby temu zapobiec, użyjemy funkcji w Cloud Functions, która będzie obserwować ścieżkę status/{uid} w Bazie danych czasu rzeczywistego. Gdy wartość Bazy danych czasu rzeczywistego się zmieni, zostanie zsynchronizowana z Cloud Firestore
, dzięki czemu stany wszystkich użytkowników będą prawidłowe.
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); // ... } }); });
Po wdrożeniu tej funkcji będziesz mieć kompletny system obecności działający
z Cloud Firestore. Poniżej znajdziesz przykład monitorowania użytkowników, którzy przechodzą do trybu online lub offline, za pomocą zapytania where().
Sieć
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); // ... } }); });
Ograniczenia
Używanie Bazy danych czasu rzeczywistego do dodawania obecności do aplikacji Cloud Firestore jest skalowalne i skuteczne, ale ma pewne ograniczenia:
- Debouncing – podczas nasłuchiwania zmian w czasie rzeczywistym w Cloud Firestore, to rozwiązanie prawdopodobnie spowoduje wiele zmian. Jeśli te zmiany powodują więcej zdarzeń, niż chcesz, ręcznie odfiltruj zdarzenia Cloud Firestore.
- Łączność – ta implementacja mierzy łączność z Bazą danych czasu rzeczywistego, a nie z Cloud Firestore. Jeśli stan połączenia z każdą bazą danych nie jest taki sam, to rozwiązanie może zgłaszać nieprawidłowy stan obecności.
- Android – na Androidzie Baza danych czasu rzeczywistego rozłącza się z
backendem po 60 sekundach braku aktywności. Brak aktywności oznacza brak otwartych detektorów lub oczekujących operacji. Aby utrzymać połączenie, zalecamy dodanie detektora zdarzeń wartości do ścieżki innej niż
.info/connected. Na przykład na początku każdej sesji możesz użyć koduFirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced(). Więcej informacji znajdziesz w artykule Wykrywanie stanu połączenia.