Используйте базу данных Firebase Realtime, чтобы реализовать простой, но мощный API-кеш для ваших мобильных приложений.
В предыдущем посте мы узнали, как использовать облачные функции Firebase, чтобы очистить ответ серверной части и сделать его более удобным для мобильных устройств.
В этом посте мы собираемся показать, как использовать Firebase Realtime Database, чтобы сохранить этот очищенный ответ, используя его в качестве кеша. Это предотвратит слишком частый вызов исходного серверного API, а также ненужные преобразования.
Зачем вообще использовать кеш?
Облачная функция, которую мы разработали в первой части, использовалась для получения необработанных данных из нашего бэкэнда и их преобразования во что-то более простое для обработки в мобильном приложении.
Но в большинстве случаев нет необходимости получать новые свежие данные каждый раз, когда их запрашивает клиент: дешевле, достаточно кэшированных данных.
Что кэшировать и как долго будет зависеть от нескольких факторов, уникальных для вашего случая:
- Бизнес-правила: возможно, ваша серверная часть производит новые данные только в определенное известное время. Например, в некоторых газетах есть только утренние, полуденные и вечерние выпуски. Или, может быть, вы знаете, что ваш бэкэнд генерирует только обновленные данные каждый час.
- Затраты: сбор данных, которые запрашивает мобильный клиент, может оказаться дорогостоящей операцией для вашей серверной части.
- Время: это связано с предыдущим моментом. Если создание запрошенных данных стоит дорого, то это наверняка будет медленным. И вы не хотите тратить время пользователей на ожидание.
Во всех этих случаях кажется разумным установить политику достоверности данных и полагаться на уже очищенные данные кеша вместо получения необработанных данных для каждого запроса.
В качестве дополнительного бонуса кэширование данных в Firebase позволяет нам совместно использовать логику кеширования между несколькими клиентами.
Сохранять очищенную модель в Firebase
Продолжая пример, представленный в первой части, мы собираемся сохранить очищенную ленту от The Guardian. Корм после очистки выглядит так:
В этом примере нужно учитывать три вещи:
- Сохранение очищенных данных в базе данных Firebase нашего проекта.
- Проверка наличия действительных кэшированных данных перед загрузкой новых.
- Политика аннулирования кеша: определение того, когда этот кеш больше не действителен.
1. Сохранение преобразованных данных в базу данных Firebase.
В этой сути вы можете найти полный код для выборки и преобразования данных, который мы сделали в первой части.
Давайте начнем с преобразования этого кода во что-то более многообещающее.
exports.fetchGuardian = functions.https.onRequest((req, res) => { return request(URL_THE_GUARDIAN) .then(data => cleanUp(data)) .then(items => response(res, items, 201)) }); function request(url) { return new Promise(function (fulfill, reject) { client.get(url, function (data, response) { fulfill(data) }) }) } function response(res, items, code) { return Promise.resolve(res.status(code) .type('application / json') .send(items)) }
Этот код эквивалентен тому, что был в предыдущем посте, но с помощью Promises поток легче понять и изменить.
Я опускаю здесь функцию cleanUp
, поскольку она не имеет отношения к делу, но посмотрите предыдущий пост, если он вам интересен.
Первое, что мы собираемся сделать, это сохранить items
в базе данных Firebase, прежде чем возвращать их клиенту. Это можно сделать, изменив предыдущий код и добавив вызов save(items)
в цепочке обещаний:
exports.fetchGuardian = functions.https.onRequest((req, res) => { return request(URL_THE_GUARDIAN) .then(data => cleanUp(data)) .then(items => save(items)) .then(items => response(res, items, 201)) }); function save(items) { return admin.database().ref('/feed/guardian') .set({ items: items }) .then(() => { return Promise.resolve(items); }) }
С помощью admin.database().ref('feed/guardian')
мы получаем ссылку на путь в нашей базе данных.
Затем set({items: items})
подталкивает массив items
к этому пути в базе данных с помощью ключа "items"
и возвращает пустое обещание.
Наконец, когда обещание выполнено (то есть процесс записи завершен), мы возвращаем новое обещание с исходным массивом items
, чтобы продолжить цепочку.
В Firebase это сгенерирует:
2. Проверка кешированных данных перед загрузкой новых
На этом этапе мы сохраняем очищенные данные в нашей базе данных, но мы ничего с ними не делаем.
Следующим шагом будет проверка наличия сохраненных данных в базе данных Firebase перед отправкой HTTP-запроса на наш сервер.
exports.fetchGuardian = functions.https.onRequest((req, res) => { return admin.database().ref('/feed/guardian') .once('value') .then(snapshot => { if (isCacheValid(snapshot)) { return response(res, snapshot.val(), 200) } else { return request(URL_THE_GUARDIAN) .then(data => cleanUp(data)) .then(items => save(items)) .then(items => response(res, items, 201)) } }) }); function isCacheValid(snapshot) { return (snapshot.exists()) }
Что изменилось с шага 1, так это то, что мы читаем из базы данных перед загрузкой фида.
Как мы уже знаем, admin.database().ref('feed/guardian')
- это путь в базе данных. С .once('value')
мы считываем значения по этому пути один раз и возвращаем с ними Promise.
Что мы делаем потом, просто:
- Если данные, прочитанные из
/feed/guardian
, действительны, мы возвращаем их (в этот момент действительные означает просто существующие) - Если данные недействительны (они не существуют), мы делаем то же самое, что и на шаге 1: считываем исходный канал, сохраняем его в базе данных Firebase и возвращаемся.
3. Политика аннулирования кеша.
Наконец, нам нужно установить правила для валидности кеша. Чтобы не усложнять этот пример, мы будем считать, что данные действительны в течение 1 часа с момента их сохранения. По истечении этого времени следующий запрос должен получить новые элементы и заменить ими кешированные.
Для этого нам нужно сэкономить время выборки вместе с элементами, когда мы их сохраняем. Нам нужно изменить нашу функцию сохранения следующим образом:
function save(items) { return admin.database().ref('/feed/guardian') .set({ date: new Date(Date.now()).toISOString(), items: items }) .then(() => { return Promise.resolve(items); }) }
Это создаст новое поле в нашей базе данных:
Последний бит - использовать эту дату, чтобы проверить, сколько лет нашим кэшированным данным.
Если кешированные данные были сохранены менее часа назад, мы будем считать их действительными. В противном случае мы аннулируем кеш, извлекая свежие данные и перезаписывая их.
function isCacheValid(snapshot) { return ( snapshot.exists() && elapsed(snapshot.val().date) < ONE_HOUR ) } function elapsed(date) { const then = new Date(date) const now = new Date(Date.now()) return now.getTime() — then.getTime() }
Все вместе
Код также доступен здесь как основной фрагмент
Будьте на связи!
Это вторая часть серии из трех статей о Firebase.
В последней части мы узнаем, как использовать Google Cloud Natural Language API из нашей облачной функции Firebase для улучшения ответа серверной части, например, для добавления анализа настроений.
— -
Найдите меня в Twitter @lgvalle, я бы хотел поговорить об Android, Firebase и облачных функциях.