В зависимости от типа приложения, которое вы создаете, вам может быть полезно определить, какие из ваших пользователей или устройств активно подключены к сети, иначе это называется определением «присутствия».
Например, если вы создаете приложение, такое как социальная сеть, или развертываете парк устройств IoT, вы можете использовать эту информацию для отображения списка друзей, которые находятся в сети и могут свободно общаться в чате, или сортировать устройства IoT по «последнему просмотру». ."
Cloud Firestore изначально не поддерживает присутствие, но вы можете использовать другие продукты Firebase для создания системы присутствия.
Решение: облачные функции с базой данных в реальном времени
Чтобы подключить Cloud Firestore к встроенной функции присутствия базы данных Firebase Realtime, используйте Cloud Functions.
Используйте базу данных в реальном времени, чтобы сообщить о состоянии подключения, а затем используйте облачные функции, чтобы отразить эти данные в Cloud Firestore.
Использование присутствия в базе данных реального времени
Во-первых, рассмотрим, как традиционная система присутствия работает в базе данных реального времени.
Интернет
// 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); }); });
Этот пример представляет собой полную систему присутствия базы данных в реальном времени. Он обрабатывает множественные отключения, сбои и так далее.
Подключение к облачному магазину Firestore
Чтобы реализовать аналогичное решение в Cloud Firestore, используйте тот же код базы данных Realtime Database, а затем используйте Cloud Functions для синхронизации базы данных Realtime Database и Cloud Firestore.
Если вы еще этого не сделали, добавьте базу данных реального времени в свой проект и включите указанное выше решение присутствия.
Далее вы синхронизируете состояние присутствия с Cloud Firestore следующими способами:
- Локально, в кеш Cloud Firestore автономного устройства, чтобы приложение знало, что оно не в сети.
- Глобально, используя облачную функцию, чтобы все другие устройства, имеющие доступ к Cloud Firestore, знали, что это конкретное устройство находится в автономном режиме.
Обновление локального кеша Cloud Firestore
Давайте взглянем на изменения, необходимые для выполнения первой задачи — обновления локального кеша Cloud Firestore.
Интернет
// ... 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); }); });
Благодаря этим изменениям мы теперь гарантировали, что локальное состояние Cloud Firestore всегда будет отражать онлайн/оффлайн статус устройства. Это означает, что вы можете прослушать документ /status/{uid}
и использовать данные, чтобы изменить свой пользовательский интерфейс, чтобы отразить статус подключения.
Интернет
userStatusFirestoreRef.onSnapshot(function(doc) { var isOnline = doc.data().state == 'online'; // ... use isOnline });
Глобальное обновление Cloud Firestore
Хотя наше приложение правильно сообщает о своем присутствии в сети, этот статус не будет точным в других приложениях Cloud Firestore, потому что наша запись «автономного» статуса является только локальной и не будет синхронизироваться при восстановлении соединения. Чтобы противостоять этому, мы будем использовать облачную функцию, которая отслеживает путь status/{uid}
в базе данных реального времени. Когда значение базы данных в реальном времени изменится, это значение будет синхронизировано с Cloud Firestore, чтобы статусы всех пользователей были правильными.
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); });
После развертывания этой функции у вас будет полная система присутствия, работающая с Cloud Firestore. Ниже приведен пример мониторинга любых пользователей, которые подключаются к сети или выходят из сети с помощью запроса where()
.
Интернет
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); // ... } }); });
Ограничения
Использование базы данных в реальном времени для добавления присутствия в ваше приложение Cloud Firestore является масштабируемым и эффективным, но имеет некоторые ограничения:
- Устранение дребезга — при прослушивании изменений в реальном времени в Cloud Firestore это решение, вероятно, вызовет несколько изменений. Если эти изменения вызывают больше событий, чем вы хотите, вручную отмените дребезг событий Cloud Firestore.
- Возможность подключения — эта реализация измеряет подключение к базе данных реального времени, а не к Cloud Firestore. Если состояние подключения к каждой базе данных неодинаково, это решение может сообщать о неправильном состоянии присутствия.
- Android — на Android база данных в реальном времени отключается от серверной части через 60 секунд бездействия. Бездействие означает отсутствие открытых слушателей или ожидающих операций. Чтобы соединение оставалось открытым, мы рекомендуем вам добавить прослушиватель событий значения к пути помимо
.info/connected
. Например, вы можете сделатьFirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced()
в начале каждого сеанса. Дополнительные сведения см. в разделе Определение состояния подключения .