تعمل تطبيقات 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 إلى الوقت المحلي المُبلغ عنه (الوقت المرجعي بالملّي ثانية) لتقدير وقت الخادم. يُرجى العِلم أنّ دقة هذا الإزاحة يمكن أن تتأثر بوقت استجابة الشبكة، لذا من المفيد بشكل أساسي رصد الاختلافات الكبيرة (> ثانية واحدة) في وقت الساعة.
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 متصلاً بالإنترنت أم لا. تضبط أجهزة العملاء هذا الموقع على "صحيح" عندما تتصل بالإنترنت، وتضبط طابعًا زمنيًا عند انقطاع الاتصال. يشير هذا الطابع الزمني إلى آخر مرة كان فيها المستخدم المحدّد متصلاً بالإنترنت.
يُرجى العِلم أنّ تطبيقك يجب أن يضع عمليات انقطاع الاتصال في قائمة الانتظار قبل وضع علامة "متصل" على المستخدم، وذلك لتجنُّب أي حالات تعارض في حال فقدان اتصال شبكة جهاز العميل قبل إرسال كلا الأمرين إلى الخادم.
في ما يلي نظام بسيط لتتبُّع حالة اتصال المستخدم:
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); } });