Как работает HTTP-кеширование
Вы уже ознакомились с процессом кеширования в контексте репликации и представляете, как выглядит этот метод горизонтального масштабирования и как его реализовать в Redis.
В этом спринте вы продолжите погружение в кеширование, чтобы закрепить знания и узнать новое. Речь пойдёт о новых паттернах и механизмах сброса кеша.
В прошлых спринтах вы рассматривали только серверное кеширование, а в этом уроке познакомитесь с клиентским. Для начала напомним общий механизм работы кеширования.
Серверное кеширование
Представьте, что вы разрабатываете веб-приложение с лентой новостей. Подобные сайты обычно имеют большое количество контента, включая статьи, изображения, видео и другие медиа.
Когда пользователь открывает приложение в браузере, его фронтенд-часть отправляет запрос на сервер для получения персонализированной ленты. На выполнение каждого запроса тратится слишком много времени: данные должны прийти от клиента на сервер, оттуда — в СУБД, которая исполняет сложный запрос, и проделать весь путь назад. В этом случае ходить в базу и каждый раз доставать из неё данные слишком затратно.
Такое долгое исполнение запроса называется высокой латентностью. Она отрицательно влияет на пользовательский опыт. Ускорить получение данных позволяет кеширование. Расскажем, как оно устроено.
Механизм работы кеширования
Каждая запись в кеше обычно содержит уникальный идентификатор, по которому можно получить или обновить данные. Когда приложение запрашивает данные, оно сначала проверяет их наличие в кеше. Если данные есть, они возвращаются непосредственно из кеша, что обеспечивает быстрый доступ.
Если данных в кеше нет, приложение должно обратиться к источнику данных, получить их и сохранить в кеш для последующего использования.
В случае с лентой новостей шаги будут такие:
- При самой первой загрузке отправляется запрос в базу данных или какой-то другой сервис, который составит ленту данного пользователя.
- Пользователю отправляется результат, а ещё заносится в кеш. Структура кеша такова, что по id пользователя можно получить ранее загруженные для него новости.
- Каждая последующая загрузка происходит в обход базы данных: новости берутся сразу из кеша.
А что, если кеш уже есть, но ещё пустой? Тогда будет так называемый «cache miss» — кеш-промах, когда приложение сначала «сходит» в кеш, ничего не найдёт и пойдёт грузить данные из базы или сервиса. А если кеш не пустой, то будет «cache hit» — кеш-попадание.
Путь запроса после добавления кеша новостной ленты на сервер будет выглядеть так:
И это вариант серверного кеширования. А есть ещё вариант и клиентского, который мы пока не рассматривали.
HTTP-кеш, или клиентское кеширование
HTTP-cache, или клиентское кеширование, не требует установки и разработки дополнительных компонентов и работает прямо в браузере.
Все HTTP-запросы сначала направляются в кеш браузера, чтобы проверить, существует ли действительный кешированный ответ, который можно использовать для выполнения запроса. Если есть совпадение, ответ считывается из кеша, что устраняет как задержку в сети, так и затраты на передачу данных.
Такой подход предоставляет возможность управлять кешем на уровне протокола HTTP. Это оптимизирует загрузку ресурсов, улучшает производительность приложений и уменьшает нагрузку на серверы.
Механизм работы клиентского кеширования
Для реализации такого кеша нужно учитывать HTTP-заголовки. В идеальном сценарии вы контролируете код клиента, который определяет заголовки запросов, и конфигурацию сервера, который определяет заголовки ответов.
Два основных HTTP-заголовка, связанных с кешем, — это Cache-Control и Expires. Они определяют, как и на какой период времени ресурсы будут кешироваться на стороне клиента.
✏️ Cache-Control — это самый главный заголовок для работы. Он может быть использован для указания максимального возраста кеша, режима повторного использования ресурса, проверки соответствия кеша и так далее.
Для него часто указывается длинное сложное значение вроде такого:
JSX
В таблице ниже мы перечислили основные параметры этого заголовка.
Примеры использования для различных ресурсов
Картинки:
JSX
Страницы с контентом:
JSX
Ответы API:
JSX
Сценарии использования Cache-Control
Часто обновляемый контент:
JSX
Статический контент с редкими изменениями:
JSX
Контент, требующий проверки актуальности:
JSX
Использование заголовка no-cache
Если есть заголовок no-cache, то нужно убедиться в актуальности запрашиваемого ресурса. Чтобы это сделать, клиент должен отправить условный запрос на проверку соответствия — conditional request — на сервер.
Есть два способа это сделать:
If-None-Match— клиент может отправить значение этого заголовка, равное значению ETag (entity tag), которое было получено при предыдущем запросе ресурса. Сервер может сравнить это значение с текущим ETag ресурса, чтобы определить, соответствует ли содержимое в кеше последней версии.If-Modified-Since— клиент отправляет значение этого заголовка, равное времени последней модификации ресурса, которое было получено при предыдущем запросе ресурса. Сервер сравнивает это значение с текущим временем последней модификации ресурса, чтобы определить, были ли сделаны изменения.
Когда правильно настроенный веб-сервер видит эти заголовки, он может подтвердить, соответствует ли версия ресурса, который браузер уже имеет в своём HTTP-кеше, последней версии на веб-сервере. Если есть совпадение, сервер передаст HTTP-ответ 304 Not Modified, что эквивалентно «Продолжай использовать то, что у тебя уже есть».
Казалось бы, для чего кеширование, если нужно ходить на сервер и проверять, что там ничего не поменялось? Но при отправке ответа такого типа передаётся мало данных, поэтому это намного быстрее, чем фактическая отправка обратно копии запрошенного ресурса.
Использование заголовка Expires
Этот заголовок позволяет указать конкретное время, после которого кешированный ресурс считается устаревшим и должен обновиться с помощью источника данных. Например, лента новостей или текст поста.
Значение должно быть указано в виде даты и времени GMT:
JSX
Это аналог параметра
max-age заголовка Cache-Control. Expires считается более старым способом указать время жизни кеша.💡 Параметр
max-age имеет преимущество перед Expires в том, что указание времени действия в секундах относительно текущего времени позволяет более динамически управлять кешем.Параметр
max-age обеспечивает более простую синхронизацию времени между клиентом и сервером. Заголовок Expires требует учёта и согласованности временных зон и форматов даты. Поэтому рекомендуем использовать max-age.Если в ответе с сервера установлен заголовок
Cache-Control с директивой max-age, заголовок Expires игнорируется.Области применения кеширования
Кеширование уменьшает нагрузку на базу данных или удалённые сервисы, следовательно, улучшает отзывчивость пользовательского интерфейса.
Кеш может реализовываться на разных уровнях архитектуры приложения, начиная от оперативной памяти и заканчивая удалённым распределённым хранилищем.
Когда не стоит использовать кеширование
У каждой технологии есть своя область применения, и кеширование — не исключение. Существует множество кейсов, где использование кеша может навредить:
- Данные быстро и часто меняютсяВ случае сильно динамических данных кеширование может привести к тому, что мы отображаем пользователю устаревшую информацию. Сохранять данные в кеше смысла нет, так как время жизни кеша будет меньше времени, затраченного на получение новых данных из базы.Пример: приложение мессенджера, где история сообщений обновляется в реальном времени.
- Недопустимо получить устаревшие данныеВ некоторых приложениях актуальность данных оказывается критически важной. кеширование может привести к отображению устаревших данных, что может повлечь за собой неправильные финансовые расчёты или ошибочные юридические операции. В таких случаях лучше отказаться от кеширования и потратить чуть больше времени на получение актуальных данных.Пример: банковские, финансовые, государственные системы.
- Данные редко запрашиваютсяЕсли данные в приложении редко запрашиваются или используются, кеширование может стать избыточным и привести к неоправданным затратам ресурсов.Пример: в приложении есть функция получения случайной цитаты из большой базы данных, и каждый пользователь запрашивает только одну цитату в день. кеширование всех цитат с целью обеспечить мгновенный доступ может быть излишним и занимать большой объём памяти.
- В приложении есть секретная информацияЕсли в приложении используется секретная информация (конфиденциальные данные клиентов или данные авторизации), то кеширование может представлять угрозу безопасности. кеш может быть скомпрометирован или взломан, что приведёт к неправомерному доступу к секретной информации. В таких случаях лучше избегать кеширования или использовать надёжные механизмы защиты данных.Пример: в приложении онлайн-банкинга кеширование конфиденциальных данных, таких как данные баланса счёта или личная информация клиента, может представлять серьёзную угрозу безопасности.
💡 Прибегать к кешированию просто ради внедрения модной технологии не стоит. Кеширование способно принести реальную пользу, но для этого нужно тщательно оценить потоки данных при проектировании архитектуры приложения.
Выше мы рассмотрели ситуации, где кеш использовать не нужно. Во всех остальных случаях применение кеширования положительно влияет на опыт пользователей.
Что почитать про HTTP-кеширование?
Итоги
- Долгое исполнение запроса называется высокой латентностью. Она отрицательно влияет на пользовательский опыт. Ускорить получение данных позволяет кеширование.
- HTTP-Cache, или клиентское кеширование, не требует установки или разработки дополнительных компонентов и работает прямо в браузере.
- Два основных HTTP-заголовка, связанных с кешем, это Cache-Control и Expires. Они определяют, как и на какой период времени ресурсы будут кешироваться на стороне клиента.
- Кеширование подходит не для всех ситуаций, иногда лучше от него отказаться — к примеру, в случае мессенджеров и банковских систем.
В следующем уроке вы узнаете паттерны кеширования и научитесь выбирать подходящий под условия задачи. Там же поговорим о способах очистки кеша.