В зависимости от типа разрабатываемого вами приложения, вам может быть полезно определять, какие из ваших пользователей или устройств активно находятся в сети — иначе говоря, определять «присутствие».
Например, если вы разрабатываете приложение, похожее на социальную сеть, или развертываете парк IoT-устройств, вы можете использовать эту информацию для отображения списка друзей, которые находятся в сети и свободны для общения, или для сортировки ваших IoT-устройств по дате последнего посещения.
Cloud Firestore изначально нет поддержки отображения статуса присутствия, но вы можете использовать другие продукты Firebase для создания системы отображения статуса присутствия.
Решение: Облачные функции с базой данных реального времени
Для подключения Cloud Firestore к встроенной функции определения местоположения Firebase Realtime Database используйте Cloud Functions.
Используйте базу данных Realtime Database для отображения статуса подключения, а затем с помощью 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); }); });
Этот пример представляет собой полноценную систему отображения информации о местонахождении базы данных в режиме реального времени. Она обрабатывает многочисленные разрывы соединения, сбои и так далее.
Подключение к Cloud Firestore
Для реализации аналогичного решения в Cloud Firestore используйте тот же код Realtime Database, а затем используйте Cloud Functions для синхронизации Realtime Database и Cloud Firestore .
Если вы еще этого не сделали, добавьте Realtime Database в свой проект и включите описанное выше решение для определения присутствия.
Далее вы синхронизируете состояние присутствия с Cloud Firestore следующими способами:
- Локально, в кэш Cloud Firestore автономного устройства, чтобы приложение знало, что оно находится в автономном режиме.
- В глобальном масштабе, используя облачную функцию, можно обеспечить знание того, что данное устройство находится в автономном режиме, для всех остальных устройств, обращающихся к Cloud Firestore .
Функции, рекомендуемые в этом руководстве , не могут работать в клиентском приложении . Их необходимо развернуть в Cloud Functions for Firebase , и для их работы требуется серверная логика из Firebase Admin SDK. Подробные инструкции см. в документации Cloud Functions .
Обновление локального кэша 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} в базе данных Realtime Database. Когда значение в Realtime Database изменяется, оно синхронизируется с Cloud Firestore , чтобы статусы всех пользователей были корректными.
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); // ... } }); });
После развертывания этой функции у вас будет полноценная система мониторинга присутствия, работающая с 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 .
- Проверка подключения — данная реализация измеряет подключение к базе данных Realtime Database, а не к Cloud Firestore . Если статус подключения к каждой базе данных не совпадает, это решение может сообщать некорректное состояние присутствия.
- Android — на Android база данных Realtime Database отключается от бэкэнда через 60 секунд бездействия. Бездействие означает отсутствие открытых обработчиков событий или ожидающих операций. Чтобы поддерживать соединение открытым, мы рекомендуем добавить обработчик событий value по пути, отличному от
.info/connected. Например, вы можете использоватьFirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced()в начале каждой сессии. Для получения дополнительной информации см. раздел «Определение состояния соединения» .