Приложения Firebase работают, даже если ваше приложение временно теряет подключение к сети. Мы предоставляем несколько инструментов для мониторинга присутствия и синхронизации локального состояния с состоянием сервера, которые представлены в этом документе.
Управление присутствием
В приложениях реального времени часто бывает полезно определить, когда клиенты подключаются и отключаются. Например, вы можете пометить пользователя как «не в сети», когда его клиент отключается.
Клиенты базы данных Firebase предоставляют простые примитивы, которые вы можете использовать для записи в базу данных, когда клиент отключается от серверов базы данных Firebase. Эти обновления происходят независимо от того, отключается клиент или нет, поэтому вы можете положиться на них в очистке данных даже в случае разрыва соединения или сбоя клиента. Все операции записи, включая установку, обновление и удаление, могут быть выполнены после отключения.
Вот простой пример записи данных при отключении с помощью примитива onDisconnect
:
Web
import { getDatabase, ref, onDisconnect } from "firebase/database"; const db = getDatabase(); const presenceRef = ref(db, "disconnectmessage"); // Write a string when this client loses connection onDisconnect(presenceRef).set("I disconnected!");
Web
var presenceRef = firebase.database().ref("disconnectmessage"); // Write a string when this client loses connection presenceRef.onDisconnect().set("I disconnected!");
Как работает onDisconnect
Когда вы устанавливаете операцию onDisconnect()
, она находится на сервере Firebase Realtime Database . Сервер проверяет безопасность, чтобы убедиться, что пользователь может выполнить запрошенное событие записи, и сообщает вашему приложению, если оно недействительно. Затем сервер контролирует соединение. Если в какой-то момент время соединения истекает или оно активно закрывается клиентом Realtime Database , сервер проверяет безопасность второй раз (чтобы убедиться, что операция все еще действительна), а затем вызывает событие.
Ваше приложение может использовать обратный вызов при операции записи, чтобы убедиться, что onDisconnect
был правильно подключен:
Web
onDisconnect(presenceRef).remove().catch((err) => { if (err) { console.error("could not establish onDisconnect event", err); } });
Web
presenceRef.onDisconnect().remove((err) => { if (err) { console.error("could not establish onDisconnect event", err); } });
Событие onDisconnect
также можно отменить, вызвав .cancel()
:
Web
const onDisconnectRef = onDisconnect(presenceRef); onDisconnectRef.set("I disconnected"); // some time later when we change our minds onDisconnectRef.cancel();
Web
var onDisconnectRef = presenceRef.onDisconnect(); onDisconnectRef.set("I disconnected"); // some time later when we change our minds onDisconnectRef.cancel();
Определение состояния соединения
Для многих функций, связанных с присутствием, вашему приложению полезно знать, когда оно онлайн или офлайн. Firebase Realtime Database предоставляет специальное местоположение в /.info/connected
, которое обновляется каждый раз, когда изменяется состояние подключения клиента Firebase Realtime Database . Вот пример:
Web
import { getDatabase, ref, onValue } from "firebase/database"; const db = getDatabase(); const connectedRef = ref(db, ".info/connected"); onValue(connectedRef, (snap) => { if (snap.val() === true) { console.log("connected"); } else { console.log("not connected"); } });
Web
var connectedRef = firebase.database().ref(".info/connected"); connectedRef.on("value", (snap) => { if (snap.val() === true) { console.log("connected"); } else { console.log("not connected"); } });
/.info/connected
— это логическое значение, которое не синхронизируется между клиентами Realtime Database , поскольку это значение зависит от состояния клиента. Другими словами, если один клиент читает /.info/connected
как ложь, это не гарантирует, что другой клиент также прочтет ложь.
Обработка задержки
Временные метки сервера
Серверы Firebase Realtime Database предоставляют механизм для вставки временных меток, сгенерированных на сервере, в качестве данных. Эта функция в сочетании с onDisconnect
обеспечивает простой способ надежного отслеживания времени отключения клиента Realtime Database :
Web
import { getDatabase, ref, onDisconnect, serverTimestamp } from "firebase/database"; const db = getDatabase(); const userLastOnlineRef = ref(db, "users/joe/lastOnline"); onDisconnect(userLastOnlineRef).set(serverTimestamp());
Web
var userLastOnlineRef = firebase.database().ref("users/joe/lastOnline"); userLastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);
Перекос часов
Хотя firebase.database.ServerValue.TIMESTAMP
гораздо точнее и предпочтительнее для большинства операций чтения/записи, иногда может быть полезно оценить сдвиг часов клиента по отношению к серверам Firebase Realtime Database . Вы можете прикрепить обратный вызов к местоположению /.info/serverTimeOffset
, чтобы получить значение в миллисекундах, которое клиенты Firebase Realtime Database добавляют к локальному отчетному времени (время эпохи в миллисекундах) для оценки времени сервера. Обратите внимание, что на точность этого смещения может влиять задержка в сети, поэтому оно полезно в первую очередь для обнаружения больших (> 1 секунды) расхождений во времени.
Web
import { getDatabase, ref, onValue } from "firebase/database"; const db = getDatabase(); const offsetRef = ref(db, ".info/serverTimeOffset"); onValue(offsetRef, (snap) => { const offset = snap.val(); const estimatedServerTimeMs = new Date().getTime() + offset; });
Web
var offsetRef = firebase.database().ref(".info/serverTimeOffset"); offsetRef.on("value", (snap) => { var offset = snap.val(); var estimatedServerTimeMs = new Date().getTime() + offset; });
Пример приложения присутствия
Комбинируя операции отключения с мониторингом состояния соединения и временными метками сервера, вы можете построить систему присутствия пользователей. В этой системе каждый пользователь хранит данные в определенном месте базы данных, чтобы указать, находится ли клиент Realtime Database в сети. Клиенты устанавливают для этого местоположения значение true, когда они подключаются к сети, и метку времени, когда они отключаются. Эта временная метка указывает, когда данный пользователь в последний раз был онлайн.
Обратите внимание, что ваше приложение должно поставить в очередь операции отключения до того, как пользователь будет помечен как онлайн, чтобы избежать каких-либо условий гонки в случае, если сетевое соединение клиента будет потеряно до того, как обе команды смогут быть отправлены на сервер.
Вот простая система присутствия пользователя:
Web
import { getDatabase, ref, onValue, push, onDisconnect, set, serverTimestamp } from "firebase/database"; // Since I can connect from multiple devices or browser tabs, we store each connection instance separately // any time that connectionsRef's value is null (i.e. has no children) I am offline const db = getDatabase(); const myConnectionsRef = ref(db, 'users/joe/connections'); // stores the timestamp of my last disconnect (the last time I was seen online) const lastOnlineRef = ref(db, 'users/joe/lastOnline'); const connectedRef = ref(db, '.info/connected'); onValue(connectedRef, (snap) => { if (snap.val() === true) { // We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect) const con = push(myConnectionsRef); // When I disconnect, remove this device onDisconnect(con).remove(); // Add this device to my connections list // this value could contain info about the device or a timestamp too set(con, true); // When I disconnect, update the last time I was seen online onDisconnect(lastOnlineRef).set(serverTimestamp()); } });
Web
// Since I can connect from multiple devices or browser tabs, we store each connection instance separately // any time that connectionsRef's value is null (i.e. has no children) I am offline var myConnectionsRef = firebase.database().ref('users/joe/connections'); // stores the timestamp of my last disconnect (the last time I was seen online) var lastOnlineRef = firebase.database().ref('users/joe/lastOnline'); var connectedRef = firebase.database().ref('.info/connected'); connectedRef.on('value', (snap) => { if (snap.val() === true) { // We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect) var con = myConnectionsRef.push(); // When I disconnect, remove this device con.onDisconnect().remove(); // Add this device to my connections list // this value could contain info about the device or a timestamp too con.set(true); // When I disconnect, update the last time I was seen online lastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP); } });