แนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการโทเค็นการลงทะเบียน FCM

หากคุณใช้ FCM API เพื่อสร้างคำขอส่งโดยทางโปรแกรม คุณอาจพบว่าเมื่อเวลาผ่านไป คุณกำลังสูญเสียทรัพยากรโดยการส่งข้อความไปยังอุปกรณ์ที่ไม่ได้ใช้งานซึ่งมีโทเค็นการลงทะเบียนเก่า สถานการณ์นี้อาจส่งผลต่อข้อมูลการส่งข้อความที่รายงานในคอนโซล Firebase หรือข้อมูลที่ส่งออกไปยัง BigQuery โดยแสดงอัตราการส่งข้อความลดลงอย่างมาก (แต่ไม่ถูกต้องจริง) คู่มือนี้กล่าวถึงมาตรการบางอย่างที่คุณสามารถทำได้เพื่อช่วยให้แน่ใจว่าการกำหนดเป้าหมายข้อความมีประสิทธิภาพและการรายงานการนำส่งที่ถูกต้อง

โทเค็นการลงทะเบียนเก่าและหมดอายุ

โทเค็นการลงทะเบียนเก่าคือโทเค็นที่เกี่ยวข้องกับอุปกรณ์ที่ไม่ได้ใช้งานซึ่งไม่ได้เชื่อมต่อกับ FCM เป็นเวลานานกว่าหนึ่งเดือน เมื่อเวลาผ่านไป อุปกรณ์จะมีโอกาสเชื่อมต่อกับ FCM น้อยลงเรื่อยๆ การส่งข้อความและการกระจายหัวข้อสำหรับโทเค็นเก่าเหล่านี้ไม่น่าจะถูกส่งออกไป

มีสาเหตุหลายประการที่ทำให้โทเค็นกลายเป็นรุ่นเก่าได้ ตัวอย่างเช่น อุปกรณ์ที่เชื่อมโยงกับโทเค็นอาจสูญหาย ถูกทำลาย หรือถูกจัดเก็บและถูกลืม

เมื่อโทเค็นเก่าไม่มีการใช้งานครบ 270 วัน FCM จะพิจารณาว่า โทเค็นหมดอายุ เมื่อโทเค็นหมดอายุ FCM จะทำเครื่องหมายว่าไม่ถูกต้องและปฏิเสธการส่งไป อย่างไรก็ตาม FCM จะออกโทเค็นใหม่สำหรับอินสแตนซ์ของแอปในกรณีที่อุปกรณ์เชื่อมต่ออีกครั้งและเปิดแอปซึ่งพบไม่บ่อยนัก

แนวทางปฏิบัติที่ดีที่สุดขั้นพื้นฐาน

มีหลักปฏิบัติพื้นฐานบางประการที่คุณควรปฏิบัติตามในแอปที่ใช้ FCM API เพื่อสร้างคำขอส่งโดยทางโปรแกรม แนวทางปฏิบัติที่ดีที่สุดหลักคือ:

  • รับโทเค็นการลงทะเบียนจาก FCM และจัดเก็บไว้ในเซิร์ฟเวอร์ของคุณ บทบาทที่สำคัญสำหรับเซิร์ฟเวอร์คือการติดตามโทเค็นของลูกค้าแต่ละรายและเก็บรายการโทเค็นที่ใช้งานอยู่ที่ได้รับการอัปเดต เราขอแนะนำอย่างยิ่งให้ใช้การประทับเวลาโทเค็นในโค้ดและเซิร์ฟเวอร์ของคุณ และอัปเดตการประทับเวลานี้เป็นระยะๆ
  • รักษาความสดของโทเค็นและลบโทเค็นเก่าออก นอกเหนือจากการลบโทเค็นที่ FCM พิจารณาว่าไม่ถูกต้องอีกต่อไปแล้ว คุณอาจต้องการตรวจสอบสัญญาณอื่นๆ ที่แสดงว่าโทเค็นล้าสมัยและลบออกในเชิงรุก คู่มือนี้จะกล่าวถึงตัวเลือกบางส่วนของคุณในการบรรลุเป้าหมายนี้

ดึงและจัดเก็บโทเค็นการลงทะเบียน

เมื่อเริ่มต้นแอปของคุณครั้งแรก FCM SDK จะสร้างโทเค็นการลงทะเบียนสำหรับอินสแตนซ์แอปไคลเอ็นต์ นี่คือโทเค็นที่คุณต้องรวมไว้ในคำขอส่งที่กำหนดเป้าหมายจาก API หรือเพิ่มการสมัครรับหัวข้อสำหรับการกำหนดเป้าหมายหัวข้อ

เราขอแนะนำอย่างยิ่งให้แอปของคุณดึงโทเค็นนี้เมื่อเริ่มต้นระบบครั้งแรก และบันทึกลงในเซิร์ฟเวอร์แอปของคุณควบคู่ไปกับการประทับเวลา การประทับเวลานี้ต้องได้รับการติดตั้งโดยโค้ดและเซิร์ฟเวอร์ของคุณ เนื่องจาก FCM SDK ไม่ได้จัดเตรียมไว้ให้สำหรับคุณ

นอกจากนี้ สิ่งสำคัญคือต้องบันทึกโทเค็นลงในเซิร์ฟเวอร์และอัปเดตการประทับเวลาทุกครั้งที่มีการเปลี่ยนแปลง เช่น เมื่อ:

  • แอปได้รับการกู้คืนบนอุปกรณ์ใหม่
  • ผู้ใช้ถอนการติดตั้งหรือติดตั้งแอปใหม่
  • ผู้ใช้ล้างข้อมูลแอป
  • แอปจะใช้งานได้อีกครั้งหลังจากที่ FCM โทเค็นที่มีอยู่หมดอายุแล้ว

ตัวอย่าง: จัดเก็บโทเค็นและการประทับเวลาใน Cloud Firestore

ตัวอย่างเช่น คุณสามารถใช้ Cloud Firestore เพื่อจัดเก็บโทเค็นในคอลเลกชันที่เรียกว่า fcmTokens ID เอกสารแต่ละรายการในคอลเลกชันจะสอดคล้องกับ ID ผู้ใช้ และเอกสารจะจัดเก็บโทเค็นการลงทะเบียนปัจจุบันและการประทับเวลาที่อัปเดตล่าสุด ใช้ 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

ไม่ว่าคุณจะปฏิบัติตามรูปแบบเวลาใดก็ตาม อย่าลืมอัปเดตโทเค็นเป็นระยะ ความถี่ในการอัปเดตเดือนละครั้งทำให้เกิดความสมดุลที่ดีระหว่างผลกระทบของแบตเตอรี่และการตรวจจับโทเค็นการลงทะเบียนที่ไม่ได้ใช้งาน ด้วยการรีเฟรชนี้ คุณยังมั่นใจได้ว่าอุปกรณ์ใดๆ ที่ไม่ได้ใช้งานจะรีเฟรชการลงทะเบียนเมื่อกลับมาใช้งานได้อีกครั้ง ไม่มีประโยชน์ที่จะทำรีเฟรชบ่อยกว่ารายสัปดาห์

ลบโทเค็นการลงทะเบียนเก่า

ก่อนที่จะส่งข้อความไปยังอุปกรณ์ ตรวจสอบให้แน่ใจว่าการประทับเวลาของโทเค็นการลงทะเบียนของอุปกรณ์นั้นอยู่ภายในกรอบเวลาที่ไม่อัปเดต ตัวอย่างเช่น คุณสามารถใช้ฟังก์ชันคลาวด์สำหรับ 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

ประโยชน์ของสองขั้นตอนนี้คือ Fanout ของคุณจะเกิดขึ้นเร็วขึ้น เนื่องจากมีโทเค็นเก่าให้กระจายน้อยลง และอินสแตนซ์แอปเก่าของคุณจะสมัครใหม่โดยอัตโนมัติเมื่อกลับมาใช้งานได้อีกครั้ง

วัดความสำเร็จในการจัดส่ง

เพื่อให้ได้ภาพการส่งข้อความที่แม่นยำที่สุด วิธีที่ดีที่สุดคือส่งข้อความไปยังอินสแตนซ์ของแอปที่ใช้งานอยู่เท่านั้น นี่เป็นสิ่งสำคัญอย่างยิ่งหากคุณส่งข้อความไปยังหัวข้อที่มีสมาชิกจำนวนมากเป็นประจำ หากส่วนหนึ่งของสมาชิกเหล่านั้นไม่ได้ใช้งานจริง ผลกระทบต่อสถิติการจัดส่งของคุณอาจมีนัยสำคัญเมื่อเวลาผ่านไป

ก่อนที่จะกำหนดเป้าหมายข้อความไปยังโทเค็น ให้พิจารณาสิ่งต่อไปนี้

  • Google Analytics ข้อมูลที่บันทึกใน BigQuery หรือสัญญาณการติดตามอื่นๆ ระบุว่าโทเค็นทำงานอยู่หรือไม่
  • ความพยายามในการจัดส่งก่อนหน้านี้ล้มเหลวอย่างต่อเนื่องในช่วงระยะเวลาหนึ่งหรือไม่
  • โทเค็นการลงทะเบียนได้รับการอัปเดตบนเซิร์ฟเวอร์ของคุณในเดือนที่ผ่านมาหรือไม่
  • สำหรับอุปกรณ์ Android FCM Data API รายงานเปอร์เซ็นต์ที่สูงของความล้มเหลวในการส่งข้อความเนื่องจาก droppedDeviceInactive หรือไม่

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการจัดส่ง โปรดดูที่ การทำความเข้าใจการส่งข้อความ