Лучшие практики управления регистрационными токенами FCM

Если вы используете API FCM для программного создания запросов на отправку, вы можете обнаружить, что со временем вы тратите ресурсы впустую, отправляя сообщения на неактивные устройства с устаревшими токенами регистрации. Эта ситуация может повлиять на данные о доставке сообщений, сообщаемые в консоли Firebase, или данные, экспортируемые в BigQuery, что может проявиться в виде резкого (но не действительного) снижения скорости доставки. В этом руководстве обсуждаются некоторые меры, которые вы можете предпринять, чтобы обеспечить эффективное таргетирование сообщений и действительные отчеты о доставке.

Устаревшие и просроченные регистрационные токены

Устаревшие токены регистрации — это токены, связанные с неактивными устройствами, которые не подключались к FCM более месяца. С течением времени становится все менее и менее вероятным, что устройство когда-либо снова подключится к FCM . Отправки сообщений и разветвления тем для этих устаревших токенов вряд ли когда-либо будут доставлены.

Существует несколько причин, по которым токен может устареть. Например, устройство, с которым связан токен, может быть потеряно, уничтожено или помещено на хранение и забыто.

Когда устаревшие токены достигают 270 дней бездействия, FCM будет считать их просроченными токенами . После истечения срока действия токена FCM помечает его как недействительный и отклоняет отправку на него. Однако FCM выпускает новый токен для экземпляра приложения в редких случаях, когда устройство подключается снова и приложение открывается.

Основные передовые практики

Есть несколько основных практик, которым следует следовать в любом приложении, использующем API FCM для создания запросов отправки программным способом. Основные передовые практики:

  • Извлеките регистрационные токены из FCM и сохраните их на своем сервере. Важная роль сервера — отслеживать токены каждого клиента и обновлять список активных токенов. Мы настоятельно рекомендуем реализовать временную метку токена в вашем коде и на ваших серверах и обновлять эту временную метку через регулярные промежутки времени.
  • Поддерживайте свежесть токенов и удаляйте устаревшие токены. Помимо удаления токенов, которые FCM больше не считает действительными, вы можете отслеживать другие признаки того, что токены стали устаревшими, и удалять их заранее. В этом руководстве обсуждаются некоторые из ваших вариантов достижения этого.

Извлечение и хранение регистрационных токенов

При первоначальном запуске вашего приложения FCM SDK генерирует регистрационный токен для экземпляра клиентского приложения. Это токен, который вы должны включить в целевые запросы отправки из API или добавить в подписки на темы для таргетинга тем.

Мы настоятельно рекомендуем вашему приложению извлекать этот токен при первом запуске и сохранять его на сервере приложений вместе с временной меткой . Эта временная метка должна быть реализована вашим кодом и вашими серверами, поскольку она не предоставляется вам FCM SDK.

Кроме того, важно сохранять токен на сервере и обновлять временную метку всякий раз, когда она изменяется, например, когда:

  • Приложение восстановлено на новом устройстве
  • Пользователь удаляет или переустанавливает приложение.
  • Пользователь очищает данные приложения
  • Приложение снова становится активным после того, как 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 см. в разделе Управление токенами Cloud Messaging в блоге 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, или другие сигналы отслеживания на то, что токен активен?
  • Были ли предыдущие попытки доставки постоянно неудачными в течение определенного периода времени?
  • Обновлялся ли регистрационный токен на ваших серверах за последний месяц?
  • Сообщает ли API данных FCM о высоком проценте сбоев доставки сообщений для устройств Android из-за droppedDeviceInactive ?

Дополнительную информацию о доставке см. в разделе Общие сведения о доставке сообщений .