تعمل تطبيقات 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);
Clock Skew
على الرغم من أنّ 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); } });