تعمل تطبيقات Firebase حتى إذا فقد تطبيقك اتصاله بالشبكة مؤقتًا. نحن نقدم العديد من الأدوات لمراقبة التواجد ومزامنة الحالة المحلية مع حالة الخادم، والتي تم تقديمها في هذه الوثيقة.
إدارة الحضور
في تطبيقات الوقت الفعلي، غالبًا ما يكون من المفيد اكتشاف وقت اتصال العملاء وقطع الاتصال. على سبيل المثال، قد ترغب في وضع علامة "غير متصل" على المستخدم عندما ينقطع اتصال العميل.
يوفر عملاء قاعدة بيانات Firebase عناصر أولية بسيطة يمكنك استخدامها للكتابة إلى قاعدة البيانات عندما ينقطع اتصال العميل بخوادم قاعدة بيانات Firebase. تحدث هذه التحديثات سواء تم قطع اتصال العميل بشكل نظيف أم لا، لذا يمكنك الاعتماد عليها لتنظيف البيانات حتى في حالة انقطاع الاتصال أو تعطل العميل. يمكن تنفيذ جميع عمليات الكتابة، بما في ذلك الإعداد والتحديث والإزالة، عند قطع الاتصال.
فيما يلي مثال بسيط لكتابة البيانات عند قطع الاتصال باستخدام onDisconnect
البدائي:
Web modular API
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 namespaced API
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، فإن الخادم يتحقق من الأمان مرة ثانية (للتأكد من أن العملية لا تزال صالحة) ثم يستدعي الحدث.
يمكن لتطبيقك استخدام رد الاتصال في عملية الكتابة للتأكد من إرفاق onDisconnect
بشكل صحيح:
Web modular API
onDisconnect(presenceRef).remove().catch((err) => { if (err) { console.error("could not establish onDisconnect event", err); } });
Web namespaced API
presenceRef.onDisconnect().remove((err) => { if (err) { console.error("could not establish onDisconnect event", err); } });
يمكن أيضًا إلغاء حدث onDisconnect
عن طريق استدعاء .cancel()
:
Web modular API
const onDisconnectRef = onDisconnect(presenceRef); onDisconnectRef.set("I disconnected"); // some time later when we change our minds onDisconnectRef.cancel();
Web namespaced API
var onDisconnectRef = presenceRef.onDisconnect(); onDisconnectRef.set("I disconnected"); // some time later when we change our minds onDisconnectRef.cancel();
الكشف عن حالة الاتصال
بالنسبة للعديد من الميزات المتعلقة بالتواجد، من المفيد أن يعرف تطبيقك متى يكون متصلاً بالإنترنت أو غير متصل بالإنترنت. توفر قاعدة بيانات Firebase Realtime موقعًا خاصًا على /.info/connected
والذي يتم تحديثه في كل مرة تتغير فيها حالة اتصال عميل Firebase Realtime Database. هنا مثال:
Web modular API
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 namespaced API
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 لأن القيمة تعتمد على حالة العميل. بمعنى آخر، إذا قرأ أحد العملاء /.info/connected
كـ false، فهذا لا يضمن أن العميل المنفصل سيقرأ أيضًا false.
التعامل مع الكمون
الطوابع الزمنية للخادم
توفر خوادم قاعدة بيانات Firebase Realtime آلية لإدراج الطوابع الزمنية التي تم إنشاؤها على الخادم كبيانات. توفر هذه الميزة، جنبًا إلى جنب مع onDisconnect
، طريقة سهلة لتدوين الوقت الذي تم فيه قطع اتصال عميل Realtime Database بشكل موثوق:
Web modular API
import { getDatabase, ref, onDisconnect, serverTimestamp } from "firebase/database"; const db = getDatabase(); const userLastOnlineRef = ref(db, "users/joe/lastOnline"); onDisconnect(userLastOnlineRef).set(serverTimestamp());
Web namespaced API
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 modular API
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 namespaced API
var offsetRef = firebase.database().ref(".info/serverTimeOffset"); offsetRef.on("value", (snap) => { var offset = snap.val(); var estimatedServerTimeMs = new Date().getTime() + offset; });
تطبيق الحضور النموذجي
من خلال الجمع بين عمليات قطع الاتصال ومراقبة حالة الاتصال والطوابع الزمنية للخادم، يمكنك إنشاء نظام حضور المستخدم. في هذا النظام، يقوم كل مستخدم بتخزين البيانات في موقع قاعدة البيانات للإشارة إلى ما إذا كان عميل قاعدة بيانات Realtime متصلاً بالإنترنت أم لا. يقوم العملاء بتعيين هذا الموقع على "صحيح" عند اتصالهم بالإنترنت وطابع زمني عند قطع الاتصال. يشير هذا الطابع الزمني إلى آخر مرة كان فيها المستخدم متصلاً بالإنترنت.
لاحظ أن تطبيقك يجب أن يضع عمليات قطع الاتصال في قائمة الانتظار قبل أن يتم وضع علامة على المستخدم متصلاً بالإنترنت، لتجنب أي حالات سباق في حالة فقدان اتصال شبكة العميل قبل إرسال كلا الأمرين إلى الخادم.
فيما يلي نظام حضور مستخدم بسيط:
Web modular API
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 namespaced API
// 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); } });