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