שיטות מומלצות לניהול אסימוני רישום של FCM

אם אתם משתמשים בממשקי ה-API של FCM כדי ליצור בקשות שליחה באופן פרוגרמטי, יכול להיות שתגלו שבמהלך הזמן אתם מבזבזים משאבים על ידי שליחת הודעות למכשירים לא פעילים עם אסימוני רישום לא תקפים. המצב הזה יכול להשפיע על נתוני העברת ההודעות שמדווחים במסוף Firebase או על נתונים שיוצאו ל-BigQuery, ולהופיע כתוצאה מירידה דרמטית (אבל לא תקפה בפועל) בשיעורי ההעברה. במדריך הזה מוסבר על כמה פעולות שאפשר לבצע כדי להבטיח טירגוט יעיל של הודעות ודיווח תקין על העברות.

טוקני רישום לא תקפים ותוקפם פג

טוקני רישום לא תקפים הם טוקנים שמשויכים למכשירים לא פעילים שלא חוברו ל-FCM במשך יותר מחודש. ככל שעובר הזמן, הסיכוי שהמכשיר יתחבר שוב ל-FCM הולך ופוחת. סביר להניח שהודעות שנשלחות ואתרי נושאים שמתפצלים לנושאים נוספים עם האסימונים הלא תקפים האלה לא יישלחו אף פעם.

יש כמה סיבות לכך שאסימון יכול להיות לא תקף. לדוגמה, יכול להיות שהמכשיר שאליו משויך האסימון אבד, הושמד או הועבר לאחסון ונשכח.

כשיהיו לטוקנים לא תקינים 270 ימים של חוסר פעילות, FCM יתייחס אליהם בתור אסימונים שפג תוקפם. אחרי שתוקף הטוקן פג, FCM מסמן אותו כלא תקף ומסרב לשלוח אליו הודעות. עם זאת, FCM מנפיק אסימון חדש למכונה של האפליקציה במקרה הנדיר שבו המכשיר מתחבר שוב והאפליקציה נפתחת.

שיטות מומלצות בסיסיות

יש כמה שיטות בסיסיות שכדאי לפעול לפיהן בכל אפליקציה שמשתמשת בממשקי API של FCM כדי ליצור בקשות שליחה באופן פרוגרמטי. השיטות המומלצות העיקריות הן:

  • אחזור אסימוני רישום מ-FCM ושמירתם בשרת אחד התפקידים החשובים של השרת הוא לעקוב אחרי האסימון של כל לקוח ולשמור רשימה מעודכנת של האסימונים הפעילים. מומלץ מאוד להטמיע חותמת זמן של אסימון בקוד ובשרתים, ולעדכן את חותמת הזמן הזו במרווחי זמן קבועים.
  • לשמור על עדכניות האסימונים ולהסיר אסימונים לא תקינים. בנוסף להסרת אסימונים ש-FCM כבר לא מחשיב כתקינים, כדאי לעקוב אחרי סימנים אחרים לכך שהאסימונים לא תקפים יותר ולהסיר אותם באופן יזום. במדריך הזה מפורטות כמה מהאפשרויות להשגת המטרה הזו.

אחזור ואחסון של טוקני רישום

בהפעלה הראשונית של האפליקציה, ה-SDK של FCM יוצר אסימון רישום למכונה של אפליקציית הלקוח. זהו האסימון שצריך לכלול בבקשות שליחה ממוקדות מה-API, או להוסיף למינויים לנושאים לצורך טירגוט נושאים.

מומלץ מאוד שהאפליקציה תאחזר את האסימון הזה בזמן ההפעלה הראשונית ותשמור אותו בשרת האפליקציה לצד חותמת זמן. את חותמת הזמן הזו צריך להטמיע בקוד ובשרתים שלכם, כי היא לא מסופקת על ידי ערכות ה-SDK של FCM.

בנוסף, חשוב לשמור את האסימון בשרת ולעדכן את חותמת הזמן בכל פעם שהיא משתנה, למשל:

  • האפליקציה משוחזרת במכשיר חדש
  • המשתמש מסיר את האפליקציה או מתקין אותה מחדש
  • המשתמש מנקה את נתוני האפליקציה
  • האפליקציה חוזרת להיות פעילה אחרי שתוקף האסימון הקיים של FCM יפוג

דוגמה: אחסון אסימונים חותמות זמן ב-Cloud Firestore

לדוגמה, אפשר להשתמש ב-Cloud Firestore כדי לאחסן אסימונים באוסף שנקרא fcmTokens. כל מזהה מסמך באוסף תואם למזהה משתמש, והמסמך מאחסן את אסימון הרישום הנוכחי ואת חותמת הזמן של העדכון האחרון שלו. משתמשים בפונקציה set כפי שמתואר בדוגמה הבאה ב-Kotlin:

    /**
     * Persist token to third-party servers.
     *
     * Modify this method to associate the user's FCM registration token with any server-side account
     * maintained by your application.
     *
     * @param token The new token.
     */
    private fun sendTokenToServer(token: String?) {
        // If you're running your own server, call API to send token and today's date for the user

        // Example shown below with Firestore
        // Add token and timestamp to Firestore for this user
        val deviceToken = hashMapOf(
            "token" to token,
            "timestamp" to FieldValue.serverTimestamp(),
        )
        // Get user ID from Firebase Auth or your own server
        Firebase.firestore.collection("fcmTokens").document("myuserid")
            .set(deviceToken)
    }

בכל פעם שמאחזרים אסימון, הוא נשמר ב-Cloud Firestore באמצעות קריאה ל-sendTokenToServer:

    /**
     * Called if the FCM registration token is updated. This may occur if the security of
     * the previous token had been compromised. Note that this is called when the
     * FCM registration token is initially generated so this is where you would retrieve the token.
     */
    override fun onNewToken(token: String) {
        Log.d(TAG, "Refreshed token: $token")

        // If you want to send messages to this application instance or
        // manage this apps subscriptions on the server side, send the
        // FCM registration token to your app server.
        sendTokenToServer(token)
    }
        var token = Firebase.messaging.token.await()

        // Check whether the retrieved token matches the one on your server for this user's device
        val preferences = this.getPreferences(Context.MODE_PRIVATE)
        val tokenStored = preferences.getString("deviceToken", "")
        lifecycleScope.launch {
            if (tokenStored == "" || tokenStored != token)
            {
                // If you have your own server, call API to send the above token and Date() for this user's device

                // Example shown below with Firestore
                // Add token and timestamp to Firestore for this user
                val deviceToken = hashMapOf(
                    "token" to token,
                    "timestamp" to FieldValue.serverTimestamp(),
                )

                // Get user ID from Firebase Auth or your own server
                Firebase.firestore.collection("fcmTokens").document("myuserid")
                    .set(deviceToken).await()
            }
        }

שמירה על עדכניות של אסימונים והסרה של אסימונים לא תקינים

לא תמיד קל לקבוע אם אסימון הוא עדכני או לא. כדי לכסות את כל המקרים, כדאי להגדיר ערך סף לזיהוי אסימונים לא תקינים. כברירת מחדל, FCM מתייחס לאסימון כאל לא תקף אם מכונה של האפליקציה שלו לא התחברה במשך חודש. סביר להניח שאסימון מלפני יותר מחודש הוא מכשיר לא פעיל. אם המכשיר היה פעיל, האסימון שלו היה מתעדכן.

בהתאם לתרחיש לדוגמה שלכם, יכול להיות שחודש אחד קצר מדי או ארוך מדי, ולכן אתם צריכים לקבוע את הקריטריונים שמתאימים לכם.

זיהוי תשובות לא חוקיות של אסימונים מהקצה העורפי של FCM

חשוב לזהות תגובות לא חוקיות של אסימונים מ-FCM ולהגיב על כך על ידי מחיקה מהמערכת שלכם של אסימוני רישום שידועים כלא חוקיים או שפג תוקפם. ב-HTTP v1 API, הודעות השגיאה האלה עשויות להצביע על כך שבקשת השליחה שלכם טירגטה אסימונים לא חוקיים או אסימונים שפג תוקפם:

  • UNREGISTERED (HTTP 404)
  • INVALID_ARGUMENT (HTTP 400)

אם אתם בטוחים שעומס העבודה של ההודעה תקין ואתם מקבלים אחת מהתשובות האלה עבור אסימון מותאם אישית, תוכלו למחוק בבטחה את הרשומה של האסימון הזה כי הוא לא יהיה תקף יותר. לדוגמה, כדי למחוק אסימונים לא חוקיים מ-Cloud Firestore, אפשר לפרוס ולהריץ פונקציה כמו זו:

    // Registration token comes from the client FCM SDKs
    const registrationToken = 'YOUR_REGISTRATION_TOKEN';

    const message = {
    data: {
        // Information you want to send inside of notification
    },
    token: registrationToken
    };

    // Send message to device with provided registration token
    getMessaging().send(message)
    .then((response) => {
        // Response is a message ID string.
    })
    .catch((error) => {
        // Delete token for user if error code is UNREGISTERED or INVALID_ARGUMENT.
        if (errorCode == "messaging/registration-token-not-registered") {
            // If you're running your own server, call API to delete the
            token for the user

            // Example shown below with Firestore
            // Get user ID from Firebase Auth or your own server
            Firebase.firestore.collection("fcmTokens").document(user.uid).delete()
        }
    });

FCM יחזיר תשובה עם טוקן לא תקין רק אם תוקף הטוקן פג אחרי 270 ימים או אם הלקוח ביטל את הרישום שלו באופן מפורש. אם אתם צריכים לעקוב אחרי מצב 'לא עדכני' בצורה מדויקת יותר בהתאם להגדרות שלכם, תוכלו להסיר באופן יזום אסימוני רישום לא עדכניים.

עדכון האסימונים באופן קבוע

מומלץ לאחזר ולעדכן מדי פעם את כל אסימוני ההרשמה בשרת. לשם כך, צריך:

  • מוסיפים לוגיקה של אפליקציה באפליקציית הלקוח כדי לאחזר את האסימון הנוכחי באמצעות קריאת ה-API המתאימה (למשל token(completion): לפלטפורמות של Apple או getToken() ל-Android), ולאחר מכן שולחים את האסימון הנוכחי לשרת האפליקציה לצורך אחסון (עם חותמת זמן). זו יכולה להיות משימה חודשית שמוגדרת כך שתכלול את כל הלקוחות או האסימונים.
  • מוסיפים לוגיקה של שרת כדי לעדכן את חותמת הזמן של האסימון במרווחי זמן קבועים, ללא קשר לשינוי באסימון.

דוגמה ללוגיקת Android לעדכון אסימונים באמצעות WorkManager מופיעה במאמר ניהול אסימונים של העברת הודעות בענן בבלוג של Firebase.

לא משנה איזה דפוס תזמון תבחרו, חשוב לעדכן את האסימונים מדי פעם. תדירות עדכון של פעם בחודש מאפשרת איזון טוב בין ההשפעה על הסוללה לבין זיהוי אסימוני רישום לא פעילים. כך תוכלו לוודא גם שמכשיר שהפסיק להיות פעיל ירענן את הרישום שלו כשהוא יחזור להיות פעיל. אין יתרון לרענון בתדירות גבוהה יותר מאשר פעם בשבוע.

הסרת טוקני רישום לא תקינים

לפני שליחת הודעות למכשיר, חשוב לוודא שחותמת הזמן של אסימון הרישום של המכשיר נמצאת בחלון הזמן של סטטוס הלא עדכני. לדוגמה, אפשר להטמיע את Cloud Functions for Firebase כדי להריץ בדיקה יומית כדי לוודא שחותמת הזמן נמצאת בחלון זמן מוגדר של חותמות זמן לא תקפות, כמו const EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30;, ואז להסיר אסימונים לא תקפים:

exports.pruneTokens = functions.pubsub.schedule('every 24 hours').onRun(async (context) => {
  // Get all documents where the timestamp exceeds is not within the past month
  const staleTokensResult = await admin.firestore().collection('fcmTokens')
      .where("timestamp", "<", Date.now() - EXPIRATION_TIME)
      .get();
  // Delete devices with stale tokens
  staleTokensResult.forEach(function(doc) { doc.ref.delete(); });
});

ביטול הרשמה של אסימונים לא תקינים לנושאים

אם אתם משתמשים בנושאים, מומלץ גם לבטל את הרישום של אסימונים לא תקפים מהנושאים שהם רשומים אליהם. יש שני שלבים:

  1. צריך להירשם מחדש לנושאים באפליקציה פעם בחודש, וגם בכל פעם שאסימון ההרשמה משתנה. כך נוצר פתרון לתיקון עצמי, שבו המינויים מופיעים מחדש באופן אוטומטי כשאפליקציה הופכת שוב לפעילה.
  2. אם מכונה של אפליקציה לא פעילה במשך חודש אחד (או חלון הזמן שלכם לסטלות), עליכם לבטל את ההרשמה שלה לנושאים באמצעות Firebase Admin SDK כדי למחוק את המיפוי של האסימון לנושאים בקצה העורפי של FCM.

היתרון של שני השלבים האלה הוא שההפצה תתבצע מהר יותר כי יש פחות אסימונים לא תקפים להפצה, ומכונות האפליקציה הלא פעילות יירשמו מחדש באופן אוטומטי ברגע שהן יהיו פעילות שוב.

מדידת הצלחת ההעברה

כדי לקבל תמונה מדויקת ככל האפשר לגבי העברת ההודעות, מומלץ לשלוח הודעות רק למכונות של האפליקציה שבהן נעשה שימוש פעיל. חשוב במיוחד לעשות זאת אם אתם שולחים הודעות באופן קבוע לנושאים עם מספר גדול של מנויים. אם חלק מהמנויים האלה לא פעילים בפועל, ההשפעה על נתוני ההעברה עשויה להיות משמעותית לאורך זמן.

לפני שמטרגטים הודעות לאסימון, כדאי להביא בחשבון את הדברים הבאים:

  • האם מערכת Google Analytics, נתונים שתועדו ב-BigQuery או אותות מעקב אחרים מצביעים על כך שהאסימון פעיל?
  • האם ניסיונות קודמים למשלוח נכשלו באופן עקבי לאורך תקופה מסוימת?
  • האם אסימון הרישום עודכן בשרתים שלך בחודש האחרון?
  • במכשירי Android, האם FCM Data API מדווח על אחוז גבוה של כשלים בהעברת הודעות בגלל droppedDeviceInactive?

מידע נוסף על העברה זמין במאמר הסבר על העברת הודעות.