Если вы используете API-интерфейсы FCM для программного создания запросов на отправку, вы можете обнаружить, что со временем вы тратите ресурсы, отправляя сообщения на неактивные устройства с устаревшими регистрационными токенами. Эта ситуация может повлиять на данные о доставке сообщений, отображаемые в консоли Firebase, или данные, экспортированные в BigQuery, что проявляется в резком (но на самом деле недействительном) падении скорости доставки. В этом руководстве обсуждаются некоторые меры, которые вы можете предпринять, чтобы обеспечить эффективный таргетинг сообщений и достоверные отчеты о доставке.
Устаревшие и просроченные регистрационные токены
Устаревшие регистрационные токены — это токены, связанные с неактивными устройствами, которые не подключались к FCM более месяца. С течением времени вероятность повторного подключения устройства к FCM становится все менее и менее вероятной. Отправка сообщений и разветвление тем для этих устаревших токенов вряд ли когда-либо будут доставлены.
Существует несколько причин, по которым токен может устареть. Например, устройство, с которым связан токен, может быть потеряно, уничтожено или помещено в хранилище и забыто.
Когда устаревшие токены достигнут 270 дней бездействия, FCM будет считать их токенами с истекшим сроком действия . По истечении срока действия токена FCM помечает его как недействительный и отклоняет отправку на него. Однако FCM выдает новый токен для экземпляра приложения в том редком случае, когда устройство снова подключается и приложение открывается.
Основные рекомендации
Существует несколько фундаментальных практик, которым следует следовать в любом приложении, использующем API-интерфейсы FCM для программного создания запросов на отправку. Основными передовыми практиками являются:
- Получите регистрационные токены из FCM и сохраните их на своем сервере. Важная роль сервера — отслеживать токен каждого клиента и обновлять список активных токенов. Мы настоятельно рекомендуем внедрить временную метку токена в ваш код и на ваши серверы и регулярно обновлять эту временную метку.
- Поддерживайте актуальность токенов и удаляйте устаревшие токены. Помимо удаления токенов, которые FCM больше не считает действительными, вы можете отслеживать другие признаки того, что токены устарели, и удалять их заранее. В этом руководстве обсуждаются некоторые варианты достижения этой цели.
Получить и сохранить регистрационные токены
При первом запуске вашего приложения пакет SDK FCM создает токен регистрации для экземпляра клиентского приложения. Это токен, который вы должны включить в целевые запросы на отправку из 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 и реагируйте, удаляя из своей системы все регистрационные токены, которые, как известно, недействительны или срок действия которых истек. При использовании API HTTP v1 эти сообщения об ошибках могут указывать на то, что ваш запрос на отправку нацелен на недействительные токены или токены с истекшим сроком действия:
-
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(); });
});
Отменить подписку на устаревшие токены из тем
Если вы используете темы, вы также можете отменить регистрацию устаревших токенов в темах, на которые они подписаны. Это включает в себя два шага:
- Ваше приложение должно повторно подписываться на темы раз в месяц и при каждом изменении токена регистрации. Это создает самовосстанавливающееся решение, при котором подписки автоматически появляются снова, когда приложение снова становится активным.
- Если экземпляр приложения простаивает в течение одного месяца (или вашего собственного окна устаревания), вам следует отказаться от подписки на него в темах с помощью Firebase Admin SDK, чтобы удалить токен для сопоставления тем из серверной части FCM .
Преимущество этих двух шагов заключается в том, что ваши разветвления будут происходить быстрее, поскольку будет меньше устаревших токенов, которые можно разветвлять, а ваши устаревшие экземпляры приложений будут автоматически переподписываться, как только они снова станут активными.
Измеряйте успех доставки
Чтобы получить наиболее точную картину доставки сообщений, лучше всего отправлять сообщения только активно используемым экземплярам приложения. Это особенно важно, если вы регулярно отправляете сообщения в темы с большим количеством подписчиков; если часть этих подписчиков на самом деле неактивна, влияние на вашу статистику доставки может со временем оказаться значительным.
Прежде чем направлять сообщения на токен, подумайте:
- Указывают ли Google Analytics, данные, собранные в BigQuery, или другие сигналы отслеживания, что токен активен?
- Были ли предыдущие попытки доставки неизменно неудачными в течение определенного периода времени?
- Обновлялся ли регистрационный токен на ваших серверах за последний месяц?
- Для устройств Android сообщает ли API данных FCM о высоком проценте сбоев доставки сообщений из-за
droppedDeviceInactive
?
Дополнительные сведения о доставке см. в разделе Общие сведения о доставке сообщений .