برنامههای 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 را false بخواند، هیچ تضمینی وجود ندارد که کلاینت جداگانه نیز false را بخواند.
مدیریت تأخیر
مهرهای زمانی سرور
سرورهای 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 به زمان گزارش شده محلی (زمان epoch به میلی ثانیه) اضافه میکنند تا زمان سرور را تخمین بزنند، بر حسب میلی ثانیه، به دست آورید. توجه داشته باشید که دقت این انحراف میتواند تحت تأثیر تأخیر شبکه قرار گیرد و بنابراین در درجه اول برای کشف اختلافات بزرگ (> 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); } });