Что такое время кэширования посылки
11 видов кэширования для современного сайта
Автор данной статьи не встречал структурированной обзорной информации о важных этапах кэширования, поэтому ему хотелось бы поделиться наработанным опытом в этой области, соединить воедино всю основную информацию по данному вопросу, а также рассмотреть плюсы и минусы каждого вида кэширования.
В первую очередь, хотелось бы пояснить, что кэширование – это одна из наиболее важных составляющих любого проекта. В частности, это единственный способ сделать больше и быстрее при использовании ограниченных ресурсов. А, как известно, ресурсы всегда ограничены: как серверные, так и пользовательские.
Основной проблематикой кэширования является быстрота реакции на запросы к основным системам хранения и обработки входящей и исходящей структурированной информации.
Представьте, что необходимо осуществить быструю передачу информации, однако скорость доступа к данным крайне низкая. Или другая ситуация: скорость хорошая, но мало доступной памяти или ширина канала недостаточная, или процессорные и дисковые факторы мешают осуществить задачу. В этом случае кэширование – это единственный выход из ситуации.
Виды кэширования
Кэширование (или кэш) – это некий промежуточный буфер, в котором хранятся данные. Благодаря кэшированию страница сайта не воссоздается заново для каждого пользователя. Кэширование позволяет осуществлять работу с большим количеством данных в максимально сжатые сроки и при ограниченных ресурсах (серверных и пользовательских).
Необходимо понимать, что работу с данными можно производить как на стороне клиента, так и на сервере. Притом, серверная обработка данных централизована и имеет ряд несомненных преимуществ (особенно для службы поддержки).
Существует несколько видов кэширования, предлагаем рассмотреть каждый вид, его особенности и рекомендации по применению:
1. Браузерное кэширование или клиентское кэширование
Представляет собой составление для браузера команды использовать имеющуюся кэшированную копию. Работа такого кэширования основана на том, что при повторном посещении, браузеру отдаётся заголовок 304 Not Modified, а сама страница или картинка загружаются из локального пользовательского кэша. Получается, что вы экономите на трафике между браузером посетителя и хостингом сайта. Соответственно, страница вашего сайта начинает загружаться быстрее.
1.1 Кэширование файлов и картинок
Браузерное кэширование как нельзя лучше подходит для сайтов, содержащих большое количество изображений: картинка не скачивается каждый раз при открытии сайта, а просто загружается через кэш браузера.
Это первый уровень кэширования, который состоит в отдаче заголовка «expired» и заголовка «304 Not Modified». Наиболее эффективным считается кэширование на 2 недели.
Однако в данном случае есть важный нюанс: если изображение на сайте меняется, то браузер узнает об этом не сразу, а только если выждать expiry или сбросить кэш в самом браузере. Это не очень эффективно, если файл постоянно изменяется и необходимо постоянно отдавать его актуальную версию.
1.2 Кэширование https
Специальные заголовки вида strict-security. Позволяет браузеру всегда обращаться по https к выбранному домену. Сохраняет это состояние довольно жёстко и, в случае отмены этого вида кэша, браузер ещё довольно долго будет пытаться загрузить страницу по https, при этом игнорируя текущие заголовки.
1.3 Кэширование центра сертификации
Так называемый, stamp центра сертификации.
Данный вид кэширования считается обязательным для применения, если вы не хотите, чтобы пользователи вашего сайта ждали, когда центр сертификации (а это некий сервер, который отвечает за достоверность вашего сертификата) обработает запрос от браузера пользователя и подтвердит, что ваш сайт действительно подтверждён им.
1.4 Кэширование страниц
Когда страница уже сгенерирована, нужно постоянно отслеживать ее актуальность. Для этого вы должны использовать серверный кэш с отслеживанием времени изменения отдельных частей страницы (если страница строится из множества динамически генерируемых блоков). При таком подходе в каждом ответе от сервера установлены специальные заголовки, обозначающие время изменения страницы, которые затем отправляются браузером пользователя при повторном обращении к странице сайта. Сервер при получении таких заголовков можем проанализировать текущее состояние страницы (возможно, даже отрисовать её), но вместо содержимого страницы отдать заголовок «304 Not Modified», что для пользовательского браузера будет означать, что можно показать страницу из своего (браузера пользователя) кэша.
Конечно, можно отправлять соответствующие заголовки без использования серверного отслеживания кэша, но в таком случае большинство пользователей получат обновление контента страницы довольно поздно. При таком подходе браузер иногда опрашивает сервер для получения обновлений, но периодичность и правила для каждого браузера настраиваются его разработчиком, поэтому надеяться на то, что ваши пользователи получат обновления вовремя, не приходится.
Как правило, кэш подразделяется по типу пользователей:
— для авторизованных;
— для неавторизованных.
Данное разделение обусловлено уникальностью контента, для каждого авторизованного пользователя и общностью контента для гостевых пользователей. В большинстве сайтов не авторизованный пользователь не может изменять содержимое сайта, а значит и влиять на его содержимое.
Браузерный кэш позволяет экономить трафик и время, затрачиваемое на загрузку страниц. Но для достижения эффекта экономии, пользователь должен хотя бы один раз посетить нашу страницу, а это означает, что нагрузка на серверные ресурсы уменьшится, но не значительно.
2. Серверное кэширование
Под серверным кэшированием понимаются все виды кэширования, при котором данные хранятся на серверной стороне. Эти данные не доступны клиентским браузерам. Кэш создаётся и хранится по принципу «один ко многим» (многие, в данном случае, — это клиентские устройства).
2.1 Кэширование страницы целиком
Наиболее эффективный кэш. Чем он интересен? Самое большое его достоинство в том, что отдача страницы происходит практически в момент обращения, как следствие – это возможность обработки миллионов запросов даже на самом слабом сервере со скоростью работы памяти и с незначительным задействованием процессора.
Пожалуй, любой когда-либо мечтал о сайте, работающем со скоростью «ping» или быстрее.
Но и у этого типа кэша есть свои минусы: например, невозможность кэшировать страницы для авторизованного пользователя, либо пользователя, содержимое страницы которого зависит от текущих переменных пользователя.
Используйте этот кэш, если серверу известны все статичные состояния внешних данных, такие как: uri, get (без дополнительных параметров), пользователь не авторизован — то есть, фактически, это идеальное состояние страницы для гостевых пользователей. Учитывайте тот факт, что при таком кэшировании архитектура сайта или приложения всегда должна однотипно обрабатывать входящие запросы и отдавать однотипные ответы. Такое состояние есть в любом приложении или сайте, его нужно лишь отследить и применить к нему кэш.
Кэширование страниц целиком, чаще всего, применяют в каких-то экстренных случаях, при этом кэш страниц сохраняется на заранее указанное время (от 2 минут), в течение которого ответы от сервера однотипны (не позволяйте браузеру кэшировать это).
2.2 Кэширование результатов компиляции php-файлов
Различают как чистую компиляцию кода, так и его оптимизацию во время компилирования (подмена скриптов). Наиболее яркие примеры:
И тот и другой вид кэширования могут использоваться в проекте, но у каждого есть собственные нюансы, которые необходимо учитывать при написании кода.
2.3 Кэширование отдельных блоков страницы
Это, пожалуй, самый интересный, но и сложный вид кэширования. Тем не менее, он тоже может быть эффективным, и на его примере легче всего объяснить принципы кэширования в целом.
Необходимо отслеживать: состояние таблиц, состояние сессии пользователя, выключать ли кэширование при POST или GET запросах (http query), зависимость от текущего адреса, постоянство кэширования (при изменении предыдущих условий) или его динамическую подстройку.
Кэширование отдельных блоков страниц лучше других типов кэширования подойдёт, если вам нужно, например, уменьшить количество запросов к базе данных от реальных (авторизованных) пользователей. Кстати, при правильно заданных зависимостях, он будет работать даже эффективнее, чем все последующие виды кэширования.
Почему этот вид кэширования настолько важен? Всё дело в том, что расширение пула серверов баз данных намного более сложная задача, чем расширение пула серверов php-части сайта. Более того, php конфликты состояния кэширования решаются гораздо легче, чем конфликты при работе с множеством баз данных.
2.4 Кэширование php на основе неразделяемых ресурсов
Лучше всего подходит при стандартизации запросов, получении данных из общих ресурсов, наличии внутренних переменных, к которым php-ресурсы обращаются несколько раз при генерации страницы.
2.5 Кэширование php на основе общих ресурсов
Такое кэширование применяйте для хранения сериализированных данных. Например: конфигурационного файла, состояния таблиц, списков файловой системы.
2.6 Кэширование mysql на основе query cache
Это довольно известная и наиболее освещённая тема. Тем не менее, хотелось бы рассмотреть специфику работы с timestamp и то, как можно избежать постоянного сброса query cache.
Наверняка, вы регулярно сталкивались с ситуацией, когда необходимо отдать новые материалы, дата публикации которых уже разрешена текущим timestamp? Проще говоря,
Обзор кэширования
Кэширование помогает значительно повысить производительность приложений и снизить затраты, независимо от масштаба
Что такое кэширование?
В сфере вычислительной обработки данных кэш – это высокоскоростной уровень хранения, на котором требуемый набор данных, как правило, временного характера. Доступ к данным на этом уровне осуществляется значительно быстрее, чем к основному месту их хранения. С помощью кэширования становится возможным эффективное повторное использование ранее полученных или вычисленных данных.
Как работает кэширование?
Данные в кэше обычно хранятся на устройстве с быстрым доступом, таком как ОЗУ (оперативное запоминающее устройство), и могут использоваться совместно с программными компонентами. Основная функция кэша – ускорение процесса извлечения данных. Он избавляет от необходимости обращаться к менее скоростному базовому уровню хранения.
Небольшой объем памяти кэша компенсируется высокой скоростью доступа. В кэше обычно хранится только требуемый набор данных, причем временно, в отличие от баз данных, где данные обычно хранятся полностью и постоянно.
Обзор кэширования
ОЗУ и работающие в памяти сервисы. Поскольку ОЗУ и работающие в памяти сервисы обеспечивают высокие показатели скорости обработки запросов, или IOPS (количество операций ввода-вывода в секунду), кэширование повышает скорость извлечения данных и сокращает расходы при работе в больших масштабах. Чтобы обеспечить аналогичный масштаб работы с помощью традиционных баз данных и оборудования на базе жестких дисков, требуются дополнительные ресурсы. Использование этих ресурсов приводит к повышению расходов, но все равно не позволяет достигнуть такой низкой задержки, какую обеспечивает кэш в памяти.
Области применения. Кэш используется на разных технологических уровнях, включая операционные системы, сетевые уровни, в том числе сети доставки контента (CDN) и DNS, интернет-приложения и базы данных. С помощью кэширования можно значительно сократить задержки и повысить производительность операций ввода-вывода в секунду для многих рабочих нагрузок приложений с большой нагрузкой на чтение, например порталов для вопросов и ответов, игровых ресурсов, порталов для распространения мультимедиа и социальных сетей. Кэшировать можно результаты запросов к базам данных, вычислений, которые требовательны к ресурсам, запросы к API и ответы на них, а также веб-артефакты, например файлы HTML, JavaScript и изображений. Рабочие нагрузки, требующие больших вычислительных мощностей для обработки наборов данных, например сервисы рекомендаций и высокопроизводительное вычислительное моделирование, тоже могут эффективно использовать уровень данных в памяти в качестве кэша. В этих приложениях можно обращаться к очень большим наборам данных в режиме реального времени через кластеры машин, которые охватывают сотни узлов. Управление этими данными в дисковом хранилище является узким местом таких приложений из-за низкой скорости работы базового оборудования.
Шаблоны проектирования. В среде распределенных вычислений выделенный уровень кэширования позволяет системам и приложениям работать независимо от кэша. При этом их жизненные циклы не влияют на кэш. Кэш служит центральным уровнем, к которому могут обращаться различные несвязанные между собой системы. Он имеет собственный жизненный цикл и архитектурную топологию. Это особенно важно для систем, в которых узлы приложений можно динамически масштабировать в обе стороны. Если кэш находится на том же узле, что и приложение или системы, которые им пользуются, масштабирование может разрушить целостность кэша. Кроме того, если используются локальные кэши, это дает преимущества только локальным приложениям, которые пользуются данными. В распределенной среде кэша данные могут охватывать множество серверов кэширования и находиться в центральном расположении, удобном для всех потребителей данных.
Рекомендации по кэшированию. При реализации уровня кэша необходимо принимать во внимание достоверность кэшируемых данных. Эффективный кэш обеспечивает высокую частоту попаданий, то есть наличия в кэше запрашиваемых данных. Промах кэша происходит, когда запрашиваемых данных в кэше нет. Для удаления из кэша неактуальных данных применяются такие механизмы, как TTL (время жизни). Следует также понимать, требуется ли для среды кэширования высокая доступность. Если она необходима, можно использовать сервисы в памяти, такие как Redis. В ряде случаев уровень в памяти можно использовать как отдельный уровень хранения данных, в отличие от кэширования из основного хранилища. Чтобы решить, подходит ли такой вариант, необходимо определить для данных в сервисе в памяти соответствующие значения RTO (требуемое время восстановления, то есть сколько времени требуется системе на восстановление после сбоя) и RPO (требуемая точка восстановления, то есть последняя восстанавливаемая точка или транзакция). Для соответствия большинству требований RTO и RPO можно применять характеристики и проектные стратегии разных сервисов в памяти.
Уровень | Клиентские | DNS | Интернет | Приложение | База данных |
Пример использования | Определение IP-адреса для домена | Ускорение получения веб-контента от серверов веб-приложений Управление веб-сеансами (на стороне сервера) | Повышение производительности приложений и ускорение доступа к данным | Сокращение задержек, связанных с запросами к базе данных | |
Технологии | Управление кэшированием с помощью HTTP-заголовков (браузеры) | Серверы DNS | Управление кэшированием с помощью HTTP-заголовков, CDN, обратные прокси-серверы, веб-ускорители, хранилища пар «ключ – значение» | Хранилища пар «ключ – значение», локальные кэши | Буферы баз данных, хранилища пар «ключ – значение» |
Решения | Для браузеров | Amazon Route 53 | Amazon CloudFront, ElastiCache для Redis, ElastiCache для Memcached, решения партнеров | Инфраструктуры приложений, ElastiCache для Redis, ElastiCache для Memcached, решения партнеров | ElastiCache для Redis, ElastiCache для Memcached |
Кэширование с помощью Amazon ElastiCache
Веб-сервис Amazon ElastiCache упрощает развертывание, эксплуатацию и масштабирование в облаке хранилища или кэша в памяти. Сервис повышает производительность интернет-приложений, позволяя получать информацию из быстрых управляемых хранилищ данных, размещенных в памяти, а не только из баз данных, размещенных на дисках и работающих не так быстро. Информацию о том, как реализовать эффективную стратегию кэширования, см. в этом техническом описании по кэшированию в памяти.
Преимущества кэширования
Повышение производительности приложений
Поскольку память работает в разы быстрее диска (магнитного или SSD), чтение данных из кэша в памяти производится крайне быстро (за доли миллисекунды). Это значительно ускоряет доступ к данным и повышает общую производительность приложения.
Сокращение затрат на базы данных
Один инстанс кэша может обрабатывать тысячи операций ввода-вывода в секунду, потенциально заменяя несколько инстансов базы данных, что в результате дает снижение общих затрат. Это особенно важно, если плата взимается за пропускную способность базы данных. В таких случаях можно снизить затраты на десятки процентов.
Снижение нагрузки на серверную часть
Благодаря освобождению серверной базы данных от значительной части нагрузки на чтение, которая направляется на уровень памяти, кэширование может сократить нагрузку на базу данных и защитить ее от снижения производительности под нагрузкой и даже от сбоев при пиковых нагрузках.
Прогнозируемая производительность
Общей проблемой современных приложений является обработка пиков в использовании приложений. Примерами могут служить социальные сети во время Суперкубка или в день выборов, веб-сайты электронной коммерции в Черную пятницу и т. д. Повышенная нагрузка на базу данных приводит к повышению задержек при получении данных, и общая производительность приложения становится непредсказуемой. Эту проблему можно решить благодаря использованию кэша в памяти с высокой пропускной способностью.
Устранение проблемных мест в базах данных
Во многих приложениях небольшое подмножество данных, например профиль знаменитости или популярный продукт, может оказаться намного более востребованным, чем остальные данные. Это приводит к появлению проблемных мест в базе данных и требует избыточного выделения ее ресурсов, чтобы удовлетворить спрос на пропускную способность, которой достаточно для получения наиболее часто используемых данных. За счет хранения общих ключей в кэше в памяти можно избавиться от необходимости избыточного выделения ресурсов и обеспечить быструю и предсказуемую работу системы при обращении к самым востребованным данным.
Повышение пропускной способности операций чтения (количество операций ввода-вывода в секунду)
Помимо сокращения задержек, системы в памяти обеспечивают намного более высокую скорость выполнения запросов (количество операций ввода-вывода в секунду) по сравнению с базами данных на диске. Один инстанс, который используется как распределенный дополнительный кэш, может обслуживать сотни тысяч запросов в секунду.
Кэширование в браузерах и CDN
Разбираем HTTP-кэширование
Использование CDN долгое время было прерогативой сайтов из топ-100 Alexa. Маленьким компаниям это было или не нужно, или же они не могли себе этого позволить. Но ситуация изменилась в последние годы — появилось множество CDN с доступными вариантами оплаты, ориентированных на небольшие сайты. В этой статье мы вместе с Ульрихом Каутцом, специалистом компании Fortabit, постараемся помочь вам в освоении этого довольно простого инструмента, рассказав о различных аспектах кэширования данных с помощью CDN.
Чтобы использовать Content Delivery Network (CDN, сети доставки контента) в качестве HTTP-кэша, мы должны четко понимать, что такое HTTP-заголовок. Какие заголовки правильны? Как они работают? Как их использовать?
Зачем вообще нужны CDN?
CDN нужны для того, чтобы быстрее доставлять содержимое веб-сайта в места географически удаленные от вашего сервера. Например, ваши сервера расположены в Ирландии, а так получилось, что многие клиенты – в Австралии. Большое расстояние создает задержки и ухудшает качество соединения, и это разочаровывает ваших клиентов. Перемещение статического контента на сервера CDN в Австралию значительно улучшает качество соединения и восприятие клиентами вашего сайта.
Польза от CDN не сводится только лишь к приведенному выше примеру. CDN – это, в принципе, просто кэш на прокси-сервере. Точнее, пограничный кэш. И даже если вас несильно волнует географический аспект, кэширующий прокси CDN может значительно улучшить восприятие клиентами вашего сайта, ускорив и повысив качество доставки контента пользователям вне зависимости от их расположения глобально.
Зачем использовать прокси-кэш?
Если коротко, это забирает на себя нагрузку с вашего сервера. И поскольку кэш содержит статичный контент, то выдается он гораздо быстрее.
Предположим, у вас есть блог, на главной странице которого размещен список ранее опубликованных записей. Чтобы сформировать эту страницу, PHP-скрипт посылает запрос к базе данных и на основе полученного ответа генерирует HTML-код страницы. И так для каждого запроса, для каждого посещения. Одно исполнение скрипта и пара запросов к БД. А для тысячи визитов — тысяча запусков скрипта и пара тысяч запросов к БД. А ведь каждая операция требует ресурсов памяти и процессора – как PHP, так и запросы к базе.
Требования к ресурсам растут линейно с ростом посетителей. Звучит пугающе? Ну да, ну да… Линейно-то линейно, но до определенного предела. Ни память, ни ресурсы процессора не бесконечны. Диск тоже может обслужить только некоторое конечное количество запросов в секунду. Рано или поздно где-то образуется узкое место, и тогда становится неважно, как много еще в запасе других ресурсов – нехватка памяти тормозит работу, или процессор не справляется, хотя памяти достаточно, неважно. Сайт начинает тормозить и даже зависает, переставая отвечать на запросы. Конечно, можно масштабировать оборудование «горизонтально», добавив памяти или ядер процессору, но это усложняет ситуацию, требует денег, а в то же время есть гораздо более простое и дешевое решение.
Кэширующий прокси между сервером и пользователями позволяет преодолеть ограничения по ресурсам. Если брать вышеприведенный пример, то только первый запрос требует вызова PHP-скрипта и обращения к базе данных. Все последующие запросы могут быть обслужены при помощи кэша. А обращение к кэшу – это просто обращение к памяти, которое обслуживается довольно быстро. И все, теперь только один вызов PHP-скрипта, пара обращений к БД для тех самых тысяч запросов.
Есть различные «типы» CDN. Администратору сетей, наверное, больше всего интересно, как именно данные хранятся внутри CDN и как потом распределяются. Но данная статья ориентирована не на администраторов, а на разработчиков, потому ограничимся тем, что бывают CDN «классические», а бывают «peer-to-peer» CDN. Второй подход реализован в современных сетях.
С точки зрения разработчиков более интересно понять, как отдаются данные в CDN, как оно там потом крутится. В этом смысле есть push CDN, а есть pull CDN (от слов: «push» — «толкать» и «pull» — «тянуть»). Как понятно из названий, push CDN ждет, когда вы будете пересылать в нее данные, а pull – сама будет их скачивать от вас.
Эта статья в основном посвящена pull CDN, поскольку их гораздо проще реализовать, и такая CDN может быть размещена перед существующим уже сайтом достаточно прозрачным образом без особых специальных ухищрений.
Как работает pull CDN?
Предположим, что у вас есть сайт, расположенный, скажем, по адресу https://www.foobar.tld. В этом случае домен www.foobar.tld должен быть прикреплен к серверу CDN, а не к серверу, на котором расположен сайт. Другой домен, не публичный, скажем, direct.foobar.tld, должен указывать на сервер с сайтом. Мы будем называть его еще «исходник» (origin).
Теперь CDN принимает запросы от пользователей и либо выдает им хранящуюся в кэше информацию, либо, если ее там нет, запрашивает ее у исходного сервера и выдает ее пользователю, попутно помещая ее в кэш.
Статический и динамический контент
Приведенная выше конфигурация работает только для полностью статического контента. Статический контент, это когда данные, получаемые по запросу определенного URL, одинаковы для всех пользователей. Например, это могут быть файлы скриптов css, подгружаемые страницей с отдельного адреса. Скажем, http://www.foobar.tld/public/css/main.css, где main.css – статичный файл одинаковый для всех страниц сайта. Очень удобный для кэширования файл.
Но существует и динамический контент. Есть множество причин, по которым контент генерируется во время исполнения запроса. Например, в случае многоязычности – здесь контент генерируется в зависимости от языка браузера. Или когда контент связан с параметрами сессии: с именем зарегистрировавшегося на сайте пользователя, например. Это не кэшируется. Также сложно кэшировать быстроменяющийся контент – например, главную страницу новостного сайта.
Теперь будьте внимательны. Сейчас будет интересное, но довольно сложное для понимания место.
Заголовки кэша
Большинство, если не все pull CDN дают нам возможность решить проблему динамического контента, вводя «постраничный» контроль кэша. Для этого используются HTTP-заголовки кэша.
Первое, что надо знать о заголовках кэша, это то, что тут есть два формата заголовков – старый и новый, в смысле соответствия спецификациям HTTP/1.0 и HTTP/1.1. Здесь есть множество различных опций, и путаница со «старыми» и «новыми» заголовками часто является той причиной, по которой люди вообще боятся связываться с темой заголовков кэша.
Чтобы упростить, мы для начала рассмотрим тэги ETag и Cache-Control. Они важны оба. Некоторые CDN до сих пор используют старые заголовки (Expires, Pragma и Age), но в качестве запасного варианта: если не используются новые тэги, тогда система использует старые.
Заголовок ETag
Это очень простой тэг. Он задает версию документа. В любом виде. «101». Или «2017-22-04». Единственное правило – значение должно быть в кавычках –
Ревалидация – проверка актуальности
Теперь о практическом применении тэга ETag – проверка актуальности содержимого кэша. Давайте на время забудем о том, что у нас конфигурация прокси-исходник, и будем говорить о просто конфигурации клиент-сервер.
Далее представим себе, что клиент посылает запрос к http://www.foobar.tld/hello.txt. Сервер в ответ выдает контент со следующим заголовком:
Здесь есть два интересных для нас тэга – ETag с MD5 хэшем контента и Last-Modified с датой последней модификации hello.txt.
Теперь давайте посмотрим, как работает проверка актуальности. Когда посетитель после некоторого перерыва снова запрашивает какой-то URL, его браузер использует в запросе один из If-* заголовков. Например, If-None-Match, через который проверяется значение ETag. В ответ на заголовок с приведенным ниже содержанием браузер должен получить или сообщение, что ничего не изменилось, или полномасштабный ответ от сервера.
Ответ сервера, если ETag не изменился:
Как вы видите, сервер отвечает «304 Not Modified», а не «200 OK», без основного тела контента, побуждая браузер посетителя использовать кэшированный контент. В случае большого объема данных это дает существенную экономию.
Как разработчик, вы можете подумать: «Это не очень хорошо. Я теперь должен еще и озаботиться обработкой в моем приложении всех этих if-запросов».
Не беспокойтесь. Тут и возникает CDN. Вернемся к первоначальной конфигурации клиент прокси исходный сервер. Все эти 304 запросы здесь обрабатывает прокси, опираясь на собственный кэш. Об этом подробнее в следующем разделе, а пока давайте поговорим еще и о тэге Last-Modified.
В ряде случаев, особенно если контент статичен, текстовый файл или статичный файл html, полезным бывает использование запроса If-Not-Modified-Since: Sun, 05 Feb 2017 10:34:56 UTC, ответом на который тоже может быть 304. Это работает вообще быстро, поскольку здесь используется время, содержащееся в метке времени у файла на диске. А вот для динамического контента временная метка бесполезна.
В общем, ETag в ряде важных и распространенных случаев гораздо удобнее.
Тэг Cache—Control
Заголовок Cache-Control несколько сложнее. Он сложнее по двум причинам. Во-первых, потому, что может использоваться как в запросе, так и в ответе. В нашей статье мы будем уделять основное внимание применению его в ответах, поскольку разработчики несут ответственность именно за эту часть обмена. Во-вторых, потенциально этот тэг контролирует два типа кэша: «локальный (или приватный) кэш» и «общий кэш».
Локальный кэш – это кэш на диске той машины, на которой запущен браузер. Например, на вашем ноутбуке. Имейте в виду, что у вас, со стороны сервера, нет полного контроля над состоянием локального кэша. В конце концов, следовать вашим «предложениям» хранить информацию час, день или неделю, решает сам браузер, поэтому не доверяйте ему слишком сильно. Пользователь может вообще очищать кэш при каждом закрытии браузера, а вы ничего не будете об этом знать кроме того, что увидите возросший трафик и «вечно молодой», не успевающий устареть локальный кэш.
Публичный, или общий кэш – это то, чему посвящена данная статья: кэш между браузером и сервером. CDN. В этом случае вы как разработчик имеете полный контроль над кэшем и должны использовать это на все сто.
Во-первых, обратите внимание, что Cache-Control оперирует тремя видами директив: кэшируемость, срок жизни кэша и проверка актуальности версий.
Пример
Например, ответ на запрос файла http://www.foobar.tld/baz.jpg должен содержать в себе заголовок ETag с указанием времени последнего изменения файла, его размера, и Cache-Control заголовок с указанием срока хранения этого файла в кэше – 1 сутки.
Ответ на запрос http://www.foobar.tld/dist/css/styles.css должен содержать в себе также заголовок ETag с указанием времени последнего изменения файла и его размера, Cache-Control заголовок с указанием срока хранения этого файла в кэше – 2 часа. Кроме того, из заголовков должен быть исключен Last-Modified, и это означает, что версия будет сверяться только по полю ETag.
Cookie
Теперь, когда мы разобрались с тем, как в кэшировании используются заголовки, давайте посмотрим на то, какую роль в этом процессе играют cookie. Cookie — это заголовки HTTP-ответа. А именно: заголовок Set-Сookie. Основная цель отправки cookie пользователю – идентификация пользователя. Соответственно, каждому уникальному посетителю нужна уникальная cookie.
В контексте кэширования, если мы будем сохранять копию ответа вместе с cookie, то из кэша все пользователи получат одну и ту же cookie и будут участвовать в одной и той же пользовательской сессии. А нам это не надо.
Другой момент, для каждого пользователя может выдаваться уникальный, отличающийся контент. Самый простой пример – корзина. В зависимости от пользователя она или пуста, или содержит некий уникальный набор товаров. И это тоже не должно кэшироваться как-то одинаково для всех. Каждый должен получать свою уникальную корзину.
Но кроме этих, «плохих» сессионных cookie, есть cookie получше. Это тип cookie, которые задаются в момент исполнения, через Javascript. К примеру, так делает Google Analytics, подключаемый через Javascript. GA задает cookie, но это не влияет на генерацию контента и не входит в Set-Cookie заголовок. Даже если GA меняет страницу, например, дописывая что-то вроде «за вами следят с помощью Google Analytics», он делает это не в том коде, который кэшируется, а на лету, во время просмотра страницы в браузере.
Работа с cookie в условиях кэширования
Первое, что тут надо сделать – выяснить, как именно ваше веб-приложение (CMS, или что вы используете) работает с cookie. Используются ли они редко (только для регистрации и входа пользователя)? Встраиваются ли в каждый ответ сервера? Есть у вас вообще возможность контролировать то, когда они устанавливаются?
# 1) Включить кэширование, если COOKIE НЕ ИСПОЛЬЗУЮТСЯ.
Header set Cache-Control «public max-age=3600» «expr=-z resp(‘Set-Cookie’)
# 2) Выключить кэширование, если КУКИ ИСПОЛЬЗУЮТСЯ
Header always remove Cache-Control «expr=-n resp(‘Set-Cookie’)
# 2a) Альтернатива двум предыдущим вариантам: установить время кэширования 0, если COOKIE ИСПОЛЬЗУЮТСЯ
Подавление cookie на основе анализа path
Некоторые CMS используют брутфорс-стратегию, насыщая заголовки записями Set-Cookie где надо и где не надо. Это, конечно, может быть и обоснованным решением, например, если ваше приложение – некая засекреченная система с очень короткой длительностью сессии – например, 5 минут. Тогда да, надо в каждом запросе проверять уникальность сессии сверкой кук. Но если у вас на сайте нет ничего персонализированного, и все для всех выглядит одинаково – вам не нужны cookie вообще.
Так что будете вы использовать нижеприведенный пример или нет, зависит от вашего приложения и ваших целей. Пусть для большей ясности, мы говорим о новостном сайте. И все новости у вас публикуются как некие новостные объекты по адресу такого вот типа: http://www.foobar.tld/news/item/. Теперь мы хотим сделать так, чтобы все ответы на запросы к /news/item/ не содержали Set-Cookie заголовков, поскольку вы убедились, что эти cookie избыточны.
Для тех, кому интересно: редирект с использованием path=$1 и последующая обработка QUERY_STRING – необходимы при условии использования Apache в связи с внутренними особенностями его функционирования. If вычисляется проще, чем RewriteRule, из-за того, что REQUEST_URI и прочие данные из изначального запроса уже изменены тем же самым Rewrite.
Кэшируемость как следствие правильной разработки
Существуют особые стратегии разработки, которые делают приложение хорошо кэшируемым.
Давайте еще раз вернемся к примеру электронного магазина. Возьмем главную страницу, на которой перечислены топовые товары или что-то типа того. Топовые товары требуют для отображения много запросов к базе данных, поэтому вы бы хотели это кэшировать. Но проблема в корзине, которую надо тоже отображать на главной странице и которая уникальна для каждого посетителя. А для некоторых ее вообще нет.
Стратегия, которая позволяет обойти это – отобразить общую для всех пользователей часть страницы, а уникальную корзину генерировать отдельно и потом подгружать на страницу при помощи Javascript. В итоге вы получите то же самое, но! Но теперь можно кэшировать всю страницу вместе с тяжелыми топовыми товарами, и потом уже на нее подгружать уникальную корзину. Правда, теперь надо будет сделать два запроса, а не один.
Такую стратегию трудно реализовать на уже существующем приложении – надо лезть глубоко в недра CMS, переписывать отображаемый слой и т. п. Если идти по этому пути, то такие возможности надо закладывать в самом начале – при разработке макета страниц.
Устаревший кэш: очистка и обход
При помощи директив max-age и s-maxage вы получаете контроль над тем, сколько будут жить в кэше те или иные специфические элементы. Но этого не всегда достаточно. Эти директивы устанавливают начало отсчета на момент генерации страницы. А в этот момент вы можете и не знать еще точно, когда именно устареет этот элемент. Например, главная страница новостного сайта. Пусть на ней размещено 10 главных новостей. И вы установили для нее max-age=900 будучи уверенным, что она обновляется каждые 15 минут. Но внезапно, по тем или иным причинам, вышла внеочередная новость, раньше чем истекло 15 минут. И вам нужно удалить соответствующий кэшированный ответ уже сейчас.
Обход кэша при помощи задания версий
Очистка кэша
Как удалить один или несколько объектов из кэша CDN, зависит от особенностей конкретной реализации. Многие CDN построены на основе популярного открытого кода Varnish, и для них общая стратегия – использовать кодовое слово PURGE в запросе.
Такие запросы на очистку требуют хотя бы минимальной аутентификации, или же внесения источника запроса в некий «белый список» адресов, с которых может быть принят такой запрос.
Для удаления одного или двух элементов такой способ хорош, но могут быть ситуации, когда он подходит не идеально. Представьте себе, к примеру, блог, где на каждой странице приведена информация об авторе. Вы изменили что-то в этом авторском блоке и теперь хотите удалить из кэша все затронутые страницы. Вы, конечно, можете делать это вручную, удаляя их по одной, но для тысячи страниц это может быть утомительным. К счастью, есть более эффективный способ:
Суррогатные ключи (тэги кэша)
Название «суррогатный ключ» используется CDN провайдером Fastly. Другие провайдеры могут называть это по-другому. Популярным является вариант «тэг кэша». Varnish называет это Hashtwo/Xkey, но это название ужасно.
Но как это не называй, оно служит одной и той же цели – отмечать кэшированные ответы некими специфическими ключами, так, что вы сможете потом легко удалить их по этим ключам, даже точно не зная, что там конкретно было кэшировано.
Для примера, вот что ответит ваш исходный сервер на использование суррогатного ключа:
В этом примере ответ «помечен» тремя суррогатными ключами: top-10, company-acme and category-foodstuff. Например, это может быть один из топ-10 товаров вашего электронного магазина, он произведен компанией ACME и принадлежит категории foodstuff.
Помечая ответы суррогатными ключами, вы легко сможете потом удалить из кэша все, что помечено top-10 или company-acme или как-то еще. Легко, правда?
В заключение отметим, что для того, чтобы вы получили теоретическое представление о вопросе, этого материала достаточно. Чтобы разобраться конкретнее, надо смотреть, как устроено кэширование у конкретных CDN, которые вы собираетесь использовать.