คุณอาจพบว่าการตรวจหาผู้ใช้หรืออุปกรณ์ที่ออนไลน์อยู่หรือที่เรียกว่าการตรวจหา "สถานะ" มีประโยชน์ ทั้งนี้ขึ้นอยู่กับประเภทแอปที่คุณกำลังสร้าง
ตัวอย่างเช่น หากคุณกำลังสร้างแอปอย่างโซเชียลเน็ตเวิร์กหรือติดตั้งใช้งานกลุ่มอุปกรณ์ IoT คุณสามารถใช้ข้อมูลนี้เพื่อแสดงรายชื่อเพื่อนที่ออนไลน์และพร้อมแชท หรือจัดเรียงอุปกรณ์ IoT ตาม "เห็นล่าสุด"
Cloud Firestore ไม่รองรับการแสดงสถานะในตัว แต่คุณสามารถใช้ประโยชน์จากผลิตภัณฑ์ Firebase อื่นๆ เพื่อสร้างระบบการแสดงสถานะได้
โซลูชัน: Cloud Functions ที่มี Realtime Database
หากต้องการเชื่อมต่อ Cloud Firestore กับฟีเจอร์แสดงสถานะในตัวของฐานข้อมูลเรียลไทม์ของ Firebase ให้ใช้ Cloud Functions
ใช้ Realtime Database เพื่อรายงานสถานะการเชื่อมต่อ จากนั้นใช้ Cloud Functions เพื่อมิเรอร์ข้อมูลนั้นไปยัง Cloud Firestore
การใช้สถานะออนไลน์ใน Realtime Database
ก่อนอื่น ให้พิจารณาว่าระบบการแสดงสถานะแบบดั้งเดิมทำงานอย่างไรใน Realtime Database
เว็บ
// Fetch the current user's ID from Firebase Authentication. var uid = firebase.auth().currentUser.uid; // Create a reference to this user's specific status node. // This is where we will store data about being online/offline. var userStatusDatabaseRef = firebase.database().ref('/status/' + uid); // We'll create two constants which we will write to // the Realtime database when this device is offline // or online. var isOfflineForDatabase = { state: 'offline', last_changed: firebase.database.ServerValue.TIMESTAMP, }; var isOnlineForDatabase = { state: 'online', last_changed: firebase.database.ServerValue.TIMESTAMP, }; // Create a reference to the special '.info/connected' path in // Realtime Database. This path returns `true` when connected // and `false` when disconnected. firebase.database().ref('.info/connected').on('value', function(snapshot) { // If we're not currently connected, don't do anything. if (snapshot.val() == false) { return; }; // If we are currently connected, then use the 'onDisconnect()' // method to add a set which will only trigger once this // client has disconnected by closing the app, // losing internet, or any other means. userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() { // The promise returned from .onDisconnect().set() will // resolve as soon as the server acknowledges the onDisconnect() // request, NOT once we've actually disconnected: // https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect // We can now safely set ourselves as 'online' knowing that the // server will mark us as offline once we lose connection. userStatusDatabaseRef.set(isOnlineForDatabase); }); });
ตัวอย่างนี้เป็นระบบแสดงสถานะ Realtime Database ที่สมบูรณ์ โดยจะจัดการการตัดการเชื่อมต่อหลายครั้ง การขัดข้อง และอื่นๆ
กำลังเชื่อมต่อกับ Cloud Firestore
หากต้องการใช้โซลูชันที่คล้ายกันใน Cloud Firestore ให้ใช้โค้ด Realtime Database เดียวกัน จากนั้นใช้ Cloud Functions เพื่อซิงค์ Realtime Database กับ Cloud Firestore ให้ตรงกัน
หากยังไม่ได้เพิ่ม ให้เพิ่ม Realtime Database ลงในโปรเจ็กต์และรวมโซลูชันการแสดงสถานะข้างต้นไว้ด้วย
ถัดไป คุณจะต้องซิงค์สถานะการปรากฏตัวกับ Cloud Firestore ผ่านวิธีการต่อไปนี้
- ในเครื่อง ไปยังแคช Cloud Firestore ของอุปกรณ์ที่ออฟไลน์เพื่อให้แอปทราบว่าออฟไลน์อยู่
- ทั่วโลก โดยใช้ Cloud Function เพื่อให้อุปกรณ์อื่นๆ ทั้งหมดที่เข้าถึง Cloud Firestore ทราบว่าอุปกรณ์นี้ออฟไลน์อยู่
ฟังก์ชันที่แนะนำในบทแนะนำนี้ไม่สามารถทำงานในแอปไคลเอ็นต์ โดยต้องติดตั้งใช้งานใน Cloud Functions for Firebase และต้องใช้ตรรกะฝั่งเซิร์ฟเวอร์จาก Firebase Admin SDK ดูคำแนะนำโดยละเอียดได้ที่เอกสารประกอบของ Cloud Functions
กำลังอัปเดตแคชในเครื่องของ Cloud Firestore
มาดูการเปลี่ยนแปลงที่จำเป็นเพื่อแก้ไขปัญหาแรกกัน นั่นคือการอัปเดตแคชในเครื่องของ Cloud Firestore
เว็บ
// ... var userStatusFirestoreRef = firebase.firestore().doc('/status/' + uid); // Firestore uses a different server timestamp value, so we'll // create two more constants for Firestore state. var isOfflineForFirestore = { state: 'offline', last_changed: firebase.firestore.FieldValue.serverTimestamp(), }; var isOnlineForFirestore = { state: 'online', last_changed: firebase.firestore.FieldValue.serverTimestamp(), }; firebase.database().ref('.info/connected').on('value', function(snapshot) { if (snapshot.val() == false) { // Instead of simply returning, we'll also set Firestore's state // to 'offline'. This ensures that our Firestore cache is aware // of the switch to 'offline.' userStatusFirestoreRef.set(isOfflineForFirestore); return; }; userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() { userStatusDatabaseRef.set(isOnlineForDatabase); // We'll also add Firestore set here for when we come online. userStatusFirestoreRef.set(isOnlineForFirestore); }); });
การเปลี่ยนแปลงเหล่านี้ช่วยให้เรามั่นใจได้ว่าสถานะในเครื่องของ Cloud Firestore จะแสดงสถานะออนไลน์/ออฟไลน์ของอุปกรณ์เสมอ ซึ่งหมายความว่าคุณสามารถฟังเอกสาร /status/{uid} และใช้ข้อมูลเพื่อเปลี่ยน UI ให้แสดงสถานะการเชื่อมต่อได้
เว็บ
userStatusFirestoreRef.onSnapshot(function(doc) { var isOnline = doc.data().state == 'online'; // ... use isOnline });
กำลังอัปเดต Cloud Firestore ทั่วโลก
แม้ว่าแอปพลิเคชันของเราจะรายงานตัวตนบนโลกออนไลน์ให้ตัวเองอย่างถูกต้อง แต่สถานะนี้จะยังไม่ถูกต้องในแอป Cloud Firestore อื่นๆ เนื่องจากสถานะ "ออฟไลน์" ของเราจะเขียนขึ้นในเครื่องเท่านั้น และจะไม่ซิงค์เมื่อการเชื่อมต่อกลับมาใช้งานได้อีกครั้ง เพื่อแก้ปัญหานี้ เราจะใช้ Cloud Function ซึ่งจะคอยดูเส้นทาง status/{uid} ใน Realtime Database เมื่อค่า Realtime Database เปลี่ยนแปลง ค่าจะซิงค์กับ Cloud Firestore เพื่อให้สถานะของผู้ใช้ทั้งหมดถูกต้อง
Node.js
const functions = require('firebase-functions/v1'); const { initializeApp } = require('firebase-admin/app'); const { getFirestore } = require('firebase-admin/firestore'); initializeApp(); // Since this code will be running in the Cloud Functions environment // we call initialize Firestore without any arguments because it // detects authentication from the environment. const firestore = getFirestore(); // Create a new function which is triggered on changes to /status/{uid} // Note: This is a Realtime Database trigger, *not* Firestore. exports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate( async (change, context) => { // Get the data written to Realtime Database const eventStatus = change.after.val(); // Then use other event data to create a reference to the // corresponding Firestore document. const userStatusFirestoreRef = firestore.doc(`status/${context.params.uid}`); // It is likely that the Realtime Database change that triggered // this event has already been overwritten by a fast change in // online / offline status, so we'll re-read the current data // and compare the timestamps. const statusSnapshot = await change.after.ref.once('value'); const status = statusSnapshot.val(); functions.logger.log(status, eventStatus); // If the current timestamp for this data is newer than // the data that triggered this event, we exit this function. if (status.last_changed > eventStatus.last_changed) { return null; } // Otherwise, we convert the last_changed field to a Date eventStatus.last_changed = new Date(eventStatus.last_changed); // ... and write it to Firestore. return userStatusFirestoreRef.set(eventStatus); });
เมื่อติดตั้งใช้งานฟังก์ชันนี้แล้ว คุณจะมีระบบการแสดงตนที่สมบูรณ์ซึ่งทำงานร่วมกับ Cloud Firestore ด้านล่างนี้เป็นตัวอย่างการตรวจสอบผู้ใช้ที่ออนไลน์หรือออฟไลน์โดยใช้การค้นหา where()
เว็บ
firebase.firestore().collection('status') .where('state', '==', 'online') .onSnapshot(function(snapshot) { snapshot.docChanges().forEach(function(change) { if (change.type === 'added') { var msg = 'User ' + change.doc.id + ' is online.'; console.log(msg); // ... } if (change.type === 'removed') { var msg = 'User ' + change.doc.id + ' is offline.'; console.log(msg); // ... } }); });
ข้อจำกัด
การใช้ Realtime Database เพื่อเพิ่มสถานะออนไลน์ลงในแอป Cloud Firestore สามารถปรับขนาดได้และมีประสิทธิภาพ แต่ก็มีข้อจำกัดบางประการดังนี้
- การดีบาวน์ซ - เมื่อฟังการเปลี่ยนแปลงแบบเรียลไทม์ใน Cloud Firestore โซลูชันนี้มีแนวโน้มที่จะทริกเกอร์การเปลี่ยนแปลงหลายรายการ หากการเปลี่ยนแปลงเหล่านี้ทริกเกอร์เหตุการณ์มากกว่าที่คุณต้องการ ให้ลดจำนวนเหตุการณ์ Cloud Firestore ด้วยตนเอง
- การเชื่อมต่อ - การติดตั้งใช้งานนี้จะวัดการเชื่อมต่อกับ Realtime Database ไม่ใช่ Cloud Firestore หากสถานะการเชื่อมต่อกับฐานข้อมูลแต่ละรายการไม่เหมือนกัน โซลูชันนี้อาจรายงานสถานะการแสดงตนที่ไม่ถูกต้อง
- Android - ใน Android Realtime Database จะยกเลิกการเชื่อมต่อจากแบ็กเอนด์หลังจากไม่มีการใช้งานเป็นเวลา 60 วินาที การไม่มีการใช้งานหมายความว่าไม่มี Listener ที่เปิดอยู่หรือการดำเนินการที่รอดำเนินการ หากต้องการเปิดการเชื่อมต่อไว้ เราขอแนะนำให้คุณเพิ่ม Listener เหตุการณ์ค่าลงในเส้นทางนอกเหนือจาก
.info/connectedเช่น คุณอาจทำFirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced()ในช่วงเริ่มต้นของแต่ละเซสชัน ดูข้อมูลเพิ่มเติมได้ที่การตรวจหาสถานะการเชื่อมต่อ