Pull to refresh

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

Reading time 9 min
Views 169K
Original author: Mark Nottingham
Вторая часть довольно подробного и интересного изложения материала, касающегося кэша и его использования. Часть 1.

Автор, 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 и HTTP-заголовки

Специалисты, работающие с HTML, могут поместить определенные теги в раздел 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 ввел новый вид валидатора, названный ETag. ETag - это уникальные идентификаторы, которые генерируются сервером и изменяются каждый раз, когда запрашивается контент. Поскольку сервер управляет тем, как сгенерированы 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 соединение, вместе установки соединения на каждый запрос. Это делает ваш сайт, кажется, намного быстрее.


Обратитесь к "Заметкам о реализации" для более конкретной информации (прим. переводчика - будет в следующей части).
Tags:
Hubs:
+14
Comments 2
Comments Comments 2

Articles