Учебное пособие по кэшированию

Автор, Mark Nottingham, — признанный эксперт в области HTTP-протокола и веб-кэширования. Является председателем IETF HTTPbis Working Group. Принимал участие в редактировании HTTP/1.1, part. 6: Caching. В настоящий момент участвует в разработке HTTP/2.0.

Текст распространяется под лицензией Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License.

От переводчика: об опечатках и неточностях просьба сообщать в личку. Спасибо.


Веб-кэш располагается между одним или несколькими веб-серверами и клиентом, или множеством клиентов, и следит за входящими запросами, сохраняя при этом копии ответов — HTML-страниц, изображений и файлов (совокупно известных, как представления (representations); прим. переводчика — позвольте я буду употреблять слово “контент” — оно, на мой взгляд, не так режет слух), для собственных нужд. Затем, если поступает другой запрос с аналогичным url-адресом, кэш может использовать сохраненный прежде ответ, вместо повторного запроса к серверу.

Существует две основные причины, по которым используется веб-кэш:

1. Уменьшение времени ожидания — так как данные по запросу берутся из кэша (который располагается “ближе” к клиенту), требуется меньше времени для получения и отображения контента на стороне клиента. Это делает Веб более отзывчивым (прим. переводчика — “отзывчивым” в контексте быстроты реакции на запрос, а не эмоционально).

2. Снижение сетевого трафика — повторное использование контента снижает объем данных, передаваемых клиенту. Это, в свою очередь, экономит деньги, если клиент платит за трафик, и сохраняет низкими и более гибкими требования к пропускной способности канала.

Виды веб-кэшей

 

Кэш браузера (Browser cache)

Если вы изучите окно настроек любого современного веб-браузера (например, Internet Explorer, Safari или Mozilla), вы, вероятно, заметите параметр настройки «Кэш». Эта опция позволяет выделить область жесткого диска на вашем компьютере для хранения просмотренного ранее контента. Кэш браузера работает согласно довольно простым правилам. Он просто проверяет являются ли данные “свежими”, обычно один раз за сессию (то есть, один раз в текущем сеансе браузера).

Этот кэш особенно полезен, когда пользователь нажимает кнопку “Назад” или кликает на ссылку, чтобы увидеть страницу, которую только что просматривал. Также, если вы используете одни и те же изображения навигации на вашем сайте, они будут выбираться из браузерного кэша почти мгновенно.

Прокси-кэш (Proxy cache)

Прокси-кэш работает по аналогичному принципу, но в гораздо большем масштабе. Прокси обслуживают сотни или тысячи пользователей; большие корпорации и интернет-провайдеры часто настраивают их на своих файрволах или используют как отдельные устройства (intermediaries).

Поскольку прокси не являются частью клиента или исходного сервера, но при этом обращены в сеть, запросы должны быть к ним как-то переадресованы. Одним из способов является использование настроек браузера для того, чтобы вручную указать ему к какому прокси обращаться; другой способ — использование перехвата (interception proxy). В этом случае прокси обрабатывают веб-запросы, перенаправленные к ним сетью, так, что клиенту нет нужды настраивать их или даже знать об их существовании.

Прокси-кэши являются своего рода общей кэш-памятью (shared cache): вместо обслуживания одного человека, они работают с большим числом пользователей и поэтому очень хороши в сокращении времени ожидания и сетевого трафика. В основном, из-за того, что популярный контент запрашивается много раз.

Кэш-шлюз (Gateway Cache)

Также известные как “реверсивные прокси-кэши” (reverse proxy cache) или “суррогаты” (surrogate cache) шлюзы тоже являются посредниками, но вместо того, чтобы использоваться системными администраторами для сохранения пропускной способности канала, они (шлюзы) обычно используются веб-мастерами для того, чтобы сделать их сайты более масштабируемыми, надежными и эффективными.

Запросы могут быть перенаправлены на шлюзы рядом методов, но обычно используется балансировщик нагрузки в той или иной форме.

Сети доставки контента (content delivery networks, CDN) распространяют шлюзы по всему интернету (или некоторой его части) и отдают кэшированный контент заинтересованным веб-сайтам. Speedera и Akamai являются примерами CDN.

Это учебное пособие преимущественно сфокусировано на браузерных кэшах и прокси, но некоторая информация подходит также и тем, кому интересны шлюзы.

Почему я должен им пользоваться

Кэширование является одной из наиболее неправильно понятых технологий в интернете. Веб-мастера, в частности, боятся потерять контроль над их сайтом, потому что прокси могут “скрыть” их пользователей, сделав сложным наблюдение посещаемости.

К несчастью для них (веб-мастеров), даже если бы веб-кэша не существовало, есть слишком много переменных в интернете, чтобы гарантировать, что владельцы сайтов будут в состоянии получить точную картину того, как пользователи обращаются с сайтом. Если это является для вас большой проблемой, данное руководство научит вас как получить необходимую статистику, не делая ваш сайт “кэшененавистником”.

Другой проблемой является то, что кэш может хранить содержимое, которое устарело или просрочено.

С другой стороны, если вы ответственно подходите к проектированию вашего веб-сайта, кэш может помочь с более быстрой загрузкой и сохранением нагрузки на сервер и интернет-соединение в рамках допустимого. Разница может быть впечатляющей: загрузка сайта, не работающего с кэшем, может потребовать нескольких секунд; в то время как преимущества использования кэширования могут сделать её кажущейся мгновенной. Пользователи по достоинству оценят малое время загрузки сайта и, возможно, будут посещать его чаще.

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

CDN, с этой точки зрения, являются интересной разработкой, потому что, в отличие от многих прокси-кэшей, их шлюзы приведены в соответствие с интересами кэшируемого веб-сайта. Тем не менее, даже тогда, когда вы используете CDN, вы все равно должны учитывать, что там будет прокси и последующее кэширование в браузере.

Резюмируя, прокси и кэш браузера будут использоваться, нравится вам это или нет. Помните, если вы не настроите ваш сайт для корректного кэширования, он будет использовать настройки кэша по-умолчанию.

Как работает веб-кэш

Все виды кэшей обладают определенным набором правил, которые они используют, чтобы определить, когда брать контент из кэша, если он доступен. Некоторые из эти правил установлены протоколами (HTTP 1.0/HTTP 1.1), некоторые — администраторами кэша (пользователями браузера или администраторами прокси).

Вообще говоря, это самые общие правила (не волнуйтесь, если вы не понимаете детали, они будут объяснены ниже):

  1. Если заголовки ответа сообщают кэшу не сохранять их, он не сохранит.
  2. Если запрос авторизованный (authorized) или безопасный (то есть, HTTPS), он не будет закэширован.
  3. Кэшированный контент считается “свежим” (то есть, может быть отправлен клиенту без проверки с исходного сервера), если:
    • У него установлено время истечения или другой заголовок, контролирующий время жизни, и он еще не истек.
    • Если кэш недавно проверял контент и тот был модифицирован достаточно давно.

    Свежий контент берется непосредственно из кэша, без проверки с сервера.

  4. Если контент является устаревшим, исходному серверу будет предложено провалидировать его или сообщить кэшу, является ли имеющаяся копия по-прежнему актуальной.
  5. При определенных обстоятельствах — например, когда он отключен от сети — кэш может сохранять устаревшие ответы без проверки с исходного сервера.

Если в ответе не присутствует валидатора (ETag или Last-Modified заголовок), и он не содержит никакой явной информации о свежести, контент, обычно (но не всегда) будет считаться некэшируемым.

Свежесть (freshness) и валидация (validation) являются наиболее важными способами, с помощью которых кэш работает с контентом. Свежий контент будет доступен мгновенно из кэша; валидное же содержимое избежит повторной отправки всех пакетов, если оно не было изменено.

Как (и как не) управлять кэшем

Существует несколько инструментов, которые веб-дизайнеры и веб-мастера могут использовать для тонкой настройки того, как кэш будет работать с их сайтами. Это может потребовать от вас небольшого ознакомления с конфигурацией сервера, но оно того стоит. Для получения более детальной информации о том, как использовать эти инструменты для работы с вашим сервером, обратитесь к разделам “Реализация” (прим. переводчика — будет в следующей части) ниже.

Мета-теги HTML и HTTP-заголовки

Специалисты, работающие с HTML, могут поместить определенные теги в раздел <HEAD> HTML-документа, в котором описываются такого рода атрибуты. Мета-теги часто используются в надежде, что они помогут пометить документ как некэшируемый или устаревающий в определенное время.

Мета-теги просты в использовании, однако не очень эффективны. Это происходит потому, что они удостаиваются внимания только некоторых браузерных кэшей, но не прокси (которые почти никогда не читают HTML-документ). Хотя это может быть заманчивым — поместить мета-тег Pragma: no-cache в HTML-код страницы, но этот шаг не обязательно сохранит страницу свежей.

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

HTTP-заголовки отправляются сервером прежде HTML и рассматриваются только браузером и любым промежуточным кэшем. Заголовки типичного HTTP/1.1 ответа могут выглядеть следующим образом:

HTTP/1.1 200 OK
Date: Fri, 30 Oct 1998 13:19:41 GMT
Server: Apache/1.3.3 (Unix)
Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT
ETag: "3e86-410-3596fbbc"
Content-Length: 1040
Content-Type: text/html

HTML будет следовать за этими заголовками, отделенный пустой строкой. Смотрите разделы “Реализация” (прим. переводчика — будет в следующей части), чтобы узнать о том, как установить HTTP-заголовки.

Примечание

Если ваш сайт размещен у интернет-провайдера (ISP) или на хостинг-ферме и они не позволяют вам устанавливать произвольные HTTP-заголовки (такие как Expires и Cache-Control), упорнее выражайте недовольство; это инструменты, необходимые для вашей работы.

HTTP-заголовки Pragma (и почему они не работают)

Многие специалисты считают, что употребление HTTP-заголовока Pragma: no-cache делает контент некэшируемым. Это не всегда верно; спецификация HTTP не описывает никаких указаний для заголовков Pragma в ответе; но Pragma заголовки запроса (заголовки, которые браузер посылает серверу) были упомянуты. Хотя некоторые кэши могут учитывать эти заголовки, большинство — не будет, и их употребление не принесет никакого эффекта. Вместо этого используйте заголовки, указанные ниже.

Контроль свежести HTTP-заголовком Expires

HTTP-заголовок Expires — основной способ управления кэшем; он сообщает всем кэшам, как долго контент трактуется как свежий. По истечении этого времени, кэш всегда будет опрашивать исходный сервер, чтобы узнать, изменился ли контент. Заголовки Expires поддерживаются практически любым кэшем.

Большинство веб-серверов позволяют установить заголовки ответов Expires с помощью ряда методов. Как правило, они позволяют установить абсолютно точное время истечения; время, на основе последнего раза, когда клиент запрашивал контент (last access time); или время, основанное на дате последнего изменения документа на сервере (last modification time).

Заголовки Expires особенно хороши для кэширования статических изображений (таких как навигация и кнопки). Потому что они не изменяются часто и вы можете устанавливать им чрезвычайно долгое время истечения, делая ваш сайт более отзывчивым к пользователям. Они также полезны для управления кэшированием страниц, которые изменяются регулярно. Если вы обновляете страницу с новостями единоразово около шести часов утра, вы можете установить время истечения для контента на этот час — так кэш будет знать о том, когда необходимо получить свежую копию, без необходимости ручного обновления пользователями с помощью нажатия “Обновить”.

Отметим, что только HTTP-дата является валидным значением HTTP-заголовка Expires. Что-то помимо этого наиболее вероятно будет интерпретировано как уже истекшее, так что контент будет некэшируемым. Также, стоит напомнить, что время в HTTP-дате трактуется по Гринвичу (GMT), не в соответствии с местным часовым поясом.

Например:
Expires: Fri, 30 Oct 1998 14:19:41 GMT

Хотя заголовок Expires полезен, он имеет некоторые ограничения. Во-первых, часы веб-сервера и кэша должны быть синхронизированы, так как в расчетах присутствует время; если у них разное представление о текущих дате/времени, ожидаемый эффект не будет достигнут и кэш может ошибочно трактовать устаревший контент как свежий.

Другая проблема с Expires заключается в том, что довольно легко забыть, что вы установили некоторому контенту определенное время истечения. Если вы не обновите время в Expires, прежде чем оно придет, каждый запрос будет обращаться к серверу, увеличивая нагрузку и время ожидания.

Примечание

Очень важно быть уверенным в том, что часы вашего веб-сервера работают правильно, если вы используете заголовок Expires. Один из способов удостовериться в этом — использование сетевого протокола для синхронизации внутренних часов компьютера (Network Time Protocol, NTP); поговорите об этом с вашим системным администратором.

HTTP-заголовки Cache-Control

HTTP/1.1 ввел новый класс заголовков, заголовки ответа Cache-Control, чтобы дать веб-мастерам больший контроль над контентом и решить ограничения, связанные с Expires.

Заголовки ответов Cache-Control включают:

  • max-age=[секунды] — описывает максимальный период времени, в течение которого контент остается свежим. Аналогично Expires, эта директива указывает время, относительное моменту запроса, а не абсолютную величину. [секунды] — количество секунд от момента запроса, в течение которых вы хотите, чтобы контент трактовался как свежий.
  • s-maxage=[секунды] — подобен max-age, отличаясь тем, что применяется только к общему кэша (т.е. прокси).
  • public — помечает авторизованные запросы, как кэшируемые; это нормально, если требуется HTTP-аутентификация, ответы автоматически становятся приватными.
  • private — позволяет кэшу, который действует для определенного пользователя (т.е. в браузере) хранить ответ; общему кэшу (т.е. прокси) — нет.
  • no-cache — принуждает кэш отправлять запрос на исходный сервер каждый раз для валидации, прежде чем выдать кэшированную копию. Это полезно, когда необходимо гарантировать, что аутентификация принята во внимание (в сочетании с public) или для поддержания жесткой свежести без потери преимуществ кэширования.
  • no-store — указывает кэшу не сохранять копию контента, ни при каких условиях.
  • must-revalidate — сообщает кэшу, что он должен подчиниться любой свежей информации, что вы ему предоставляете о контенте. HTTP позволяет кэшу хранить устаревший контент при определенных условиях; упомянув этот заголовок, вы сообщаете кэшу, что вы хотите, чтобы он строго следовал вашим правилам.
  • proxy-revalidate — подобен must-revalidate, кроме того, что применяется только к прокси.

Например:
Cache-Control: max-age=3600, must-revalidate

Когда оба, и Cache-Control, и Expires — присутствуют, больший приоритет имеет Cache-Control. Если вы планируете использовать Cache-Control, вам следует ознакомиться с документацией поHTTP/1.1.

Валидаторы и валидация

В секции «Как работает кэш» мы сказали, что валидация используется серверами и кэшами для взаимодействия, когда контент был изменен. Используя её, кэш избегает необходимости скачивания контента целиком, когда он уже имеет локальную копию, но не уверен в том, что она все еще свежая.

Валидаторы очень важны; если нет ни одного и не доступна любая информация о свежести (Expiresили Cache-Control), кэш не будет хранить контент вообще.

Наиболее распространенный валидатор это время, когда документ был в последний раз именен, о чем сообщено в заголовке Last-Modified. Когда кэш хранит контент, который содержит заголовокLast-Modified, он (кэш) может использовать его для того, чтобы опросить сервер, чтобы узнать был ли изменен контент со времени его последнего просмотра, с помощью If-Modified-Since запроса.

HTTP/1.1 ввел новый вид валидатора, названный ETagETag — это уникальные идентификаторы, которые генерируются сервером и изменяются каждый раз, когда запрашивается контент. Поскольку сервер управляет тем, как сгенерированы ETag, кэш может быть уверен в том, что, еслиETag совпадают по результатам запроса If-None-Match, контент действительно совпадает.

Почти все кэши используют время из Last-Modified как валидатор; ETag также становятся распространенными.

Большинство современных веб-серверов могут автоматически генерировать и ETag, и Last-Modifiedзаголовки, чтобы использовать в качестве валидаторов для статического контента (т.е. файлов); вам не придется ничего делать. Однако, они не знают достаточно о динамическом контенте (таком, как CGI, ASP, базы данных сайтов), чтобы их генерировать (см. раздел «Написание скриптов, дружественных кэшу»).

Советы по построению дружественных кэшу сайтов

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

  • Используйте URL-адреса последовательно — это золотое правило кэширования. Если вы храните некоторый контент на разных страницах, для разных пользователей или на разных сайтах, он должен использовать один и тот же URL-адрес. Это самый простой и самый эффективный способ сделать ваш сайт дружественным кэшу. Например, если вы однажды используете “/index.html” в качестве эталона, используйте его таким образом всегда.
  • Используйте общую библиотеку изображений и других элементов и обращайтесь к ним из разных мест.
  • Храните в кэше изображения и страницы, которые редко изменяются, с помощью указания заголовка Cache-Control: max-age с большими значениями max-age.
  • Дайте кэшу возможность распознавать страницы, которые обновляются регулярно, указав соответствующий max-age или время истечения (expiration time).
  • Если ресурс (особенно скачиваемый файл) изменяется, измените его имя. Таким образом, вы можете установить время его истечения далеко в будущем и при этом гарантировать, что все еще храните актуальную версию; страница, которая хранит ссылку на него, — единственное, что потребует короткого времени истечения.
  • Не изменяйте файлы без необходимости. Если вы так поступаете, всё будет иметь обманчиво недавнюю дату в Last-Modified.
  • Используйте куки только когда необходимо — куки сложно кэшировать и они, в большинстве ситуаций, не нужны. Если вам необходимо их использовать, ограничьте сферу их применения динамическими страницами.
  • Минимизируйте использование SSL — потому как шифрованные страницы не хранятся в общем кэше; пользуйтесь ими только при необходимости и бережно относитесь к взаимодействию через SSL с изображениями.
  • Проверьте свои страницы с помощью REDbot — он поможет вам применить многие идеи из этого учебного пособия.

Написание скриптов, дружественных кэшу

По-умолчанию, большинство сценариев (прим. переводчика — в рамках данного текста слово “сценарий” имеет тот же смысл, что и слово “скрипт”) не будет возвращать валидатор (Last-Modified или ETag в заголовоке ответа) или информацию о свежести (Expires или Cache-Control). В то время как некоторые скрипты являются по-настоящему динамическими (это означает, что они возвращают разные ответы на каждый запрос), многие (такие, как поисковые движки или сайты, взаимодействующие с базами данных) могут извлечь пользу из взаимодействия с кэшем.

Вообще говоря, если скрипт производит некий вывод, который является воспроизводимым тем же запросом позднее (неважно, через минуты или дни), он должен быть закэширован. Если контент на выходе сценария изменяется только в зависимости от того, что в URL-адресе, он кэшируется; если вывод зависит от куков, информации об аутентификации или от какого-то другого внешнего фактора, вероятно кэширования не происходит.

  • Лучший способ сделать скрипт дружественным кэшу — это выгружать его содержимое в простой файл всякий раз, когда оно меняется. Веб-сервер может затем обрабатывать её как любую другую страницу, создавая и используя валидаторы, которые сделают вашу жизнь проще. Не забудьте только записывать файлы, которые были изменены, так вы сохраните времяLast-Modified.
  • Другой способ сделать скрипт кэшируемым в ограниченной форме — установить заголовки, связанные с возрастом, настолько далекими в будущем, насколько это практично. Хотя это может быть исполнено с помощью Expires, вероятно проще всего это сделать с помощьюCache-Control: max-age, который сделает запрос свежим на всем протяжении этого периода.
  • Если вы не можете этого сделать, вам придется создать валидатор, а затем ответить на If-Modified-Since и/или If-None-Match запросы. Это может быть сделано путем парсинга HTTP заголовков и последующим ответом с 304 Not Modified при необходимости. К сожалению, это не тривиальная задача.

Некоторые другие советы:

  • Не используйте POST, если это не является целесообразным. Ответы POST-методом не хранятся большинством кэшей; если вы отправляете информацию в строке запроса (через GET), кэш может хранить эту информацию на будущее.
  • Не вставляйте информацию, специфичную для пользователя, в URL-адрес, если сгенерированный контент не является полностью уникальным для этого пользователя.
  • Не рассчитывайте на все запросы пользователя, приходящие с одного хоста, потому что кэши часто работают вместе.
  • Генерируйте Content-Length заголовки ответа. Это легко сделать и позволит ответу от вашего скрипта использоваться в постоянном соединении (persistent connection). Это позволяет клиентам запрашивать несколько экземпляров контента через одно TCP/IP соединение, вместе установки соединения на каждый запрос. Это делает ваш сайт, кажется, намного быстрее.

Обратитесь к «Заметкам о реализации» для более конкретной информации (прим. переводчика — будет в следующей части).