2010-08-11 6 views
15

Если я правильно понял, то браузеры кэшируют изображения, файлы JS и т. Д. На основе имени файла. Таким образом, существует опасность, что если один такой файл будет обновлен (на сервере), браузер будет использовать вместо него кешированную копию.кеширование на стороне клиента

Обходной путь для этой проблемы состоит в том, чтобы переименовать все файлы (как часть сборки), так что имя файла содержит хеш MD5 его содержимого, например.

foo.js -> foo_AS577688BC87654.js 
me.png -> me_32126A88BC3456BB.png 

Однако, помимо переименования самих файлов, все ссылки на эти файлы должны быть изменены. Для exmaple тег, такой как <img src="me.png"/>, должен быть изменен на <img src="me_32126A88BC3456BB.png"/>.

Очевидно, что это может стать довольно сложным, особенно если учесть, что ссылки на эти файлы могут быть динамически созданы внутри серверного кода.

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

Есть ли лучшее решение?

Спасибо, Дон

+2

Это действительно проблема? Современные браузеры будут проверять, по крайней мере, один раз за сеанс, если не обновлять каждую страницу, чтобы увидеть, обновляется ли изображение или js или другой файл, на который делается ссылка, добавив заголовок if-modified-since в свой запрос. Если файл не изменен, веб-сервер вернет 304, и браузер будет использовать кешированный файл. – GrandmasterB

+0

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

+0

* на основе имени файла * - не совсем, см. ETags: http://en.wikipedia.org/wiki/HTTP_ETag. Что бы вы сделали, используется ваш хеш в качестве ETag ресурса. – Douglas

ответ

15

Лучшее решение, похоже, относится к именам файлов версий, добавляя последнее модифицированное время.

Вы можете сделать это следующим образом: добавить правила перезаписи в конфигурацию Apache, например, так:

RewriteRule ^(.+)\.(.+)\.(js|css|jpg|png|gif)$ $1.$3 

Это будет перенаправлять любой «версионную» URL к «нормальным». Идея состоит в том, чтобы сохранить ваши имена файлов одинаковыми, но извлечь выгоду из кеша. Решение добавить параметр в URL-адрес не будет оптимальным с некоторыми прокси-серверами, которые не кэшируют URL-адреса с параметрами.

Тогда вместо написания:

<img src="image.png" /> 

Просто позвоните на PHP функцию:

<img src="<?php versionFile('image.png'); ?>" /> 

С versionFile(), глядя, как это:

function versionFile($file){ 
    $path = pathinfo($file); 
    $ver = '.'.filemtime($_SERVER['DOCUMENT_ROOT'].$file).'.'; 
    echo $path['dirname'].'/'.str_replace('.', $ver, $path['basename']); 
} 

И вот оно! Браузер попросит image.123456789.png, Apache перенаправит это на image.png, так что вы будете использовать кеш во всех случаях и не будете иметь устаревшей проблемы, не имея необходимости беспокоиться о версиях имени файла ,

Вы можете увидеть подробное описание этой методики здесь: http://particletree.com/notebook/automatically-version-your-css-and-javascript-files/

+0

Это, безусловно, лучшее решение здесь. –

+0

Спасибо! :-) Но автор из партии заслуживает всякого кредита. – PJP

9

Почему бы не просто добавить строки запроса «версии» номер и обновить версию каждый раз?

foo.js -> foo.js версия = 5

Там еще немного работы во время сборки для обновления номера версии, но имена файлов не нужно менять.

+3

согласован как обычная практика. – darma

+4

Строки запроса - плохая идея, если вы заботитесь о кешировании, потому что большинство прокси-серверов не кэшируют ресурсы с помощью?? в их URL. См. Http://code.google.com/speed/page-speed/docs/caching.html. –

+2

Paul - Я думаю, что рекомендации на этой странице неполные. Раздел 13.9 RFC 2616 (спецификация HTTP 1.1), по-видимому, подразумевает, что строки запросов могут быть кэшируемыми, если есть явный заголовок Expires, поэтому, возможно, с помощью querystring в порядке. Но это не означает, что все кеши будут соблюдать это, поэтому я, вероятно, буду придерживаться самих имен файлов. –

6

переименования ресурсов является путем, хотя мы используем номер сборки и вставлять, что в имя файла вместо хеш MD5

foo.js -> foo.123.js 

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

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

1

Я также думал об этом для сайта, на котором я поддерживаю, где было бы большой работой по изменению всех ссылок. У меня есть две идеи:

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

2. Каждый раз, когда новая версия приложения развертывается в другом контексте, который содержит номер версии. например. www.site.com/app_2_6_0/ Я не уверен в этом, так как пользовательские закладки будут разбиты на каждое обновление.

3

Мы следовали аналогичной схеме с ПЭП, используя Rails и Nginx.

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

Мы добавили метод модели аватара для добавления метки к имени файла:

return "/images/#{sourcedir}/#{user.login}-#{self.updated_at.to_s(:flat_string)}.png" 

Во всех местах в коде, где были использованы аватар, мы ссылаемся этот метод, а не URL. В конфигурации Nginx, мы добавили эту перезапись:

rewrite "^/images/avatars/(.+)-[\d]{12}.png" /images/avatars/$1.png; 
rewrite "^/images/small-avatars/(.+)-[\d]{12}.png"  /images/small-avatars/$1.png; 

Это означало, если файл изменен, его URL в HTML изменился, так что браузер пользователя сделал новый запрос для файла. Когда запрос достиг Nginx, он был переписан на простое имя файла.

2

Большинство современных браузеров проверяют заголовок if-modified-since каждый раз, когда кешируемый ресурс находится в HTTP-запросе. Однако не все браузеры поддерживают заголовок if-modified-since.

Существует три способа «заставить» браузер загружать кешированный ресурс.

Вариант 1 Создайте строку запроса с версией #. src="script.js?ver=21". Недостатком является то, что многие прокси-серверы не кэшируют ресурс с помощью строк запроса. Он также требует обновления сайта для изменений.

Вариант 2 Создайте систему именования для своих файлов src="script083010.js". Однако недостатком варианта 1 является то, что для этого также требуются обновления на сайте при каждом изменении файла.

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

Вот несколько ресурсов, чтобы проверить: YahooGoogleAskApache.com

+0

Lol прочитал ваш ответ после моего, почти точно так же. +1 для hivemind. – Incognito

3

Я хотел бы предложить использовать кэширование по ETags в этой ситуации см http://en.wikipedia.org/wiki/HTTP_ETag. Затем вы можете использовать хеш в качестве этага. Запрос будет по-прежнему отправляться для каждого ресурса, но браузер будет загружать только те элементы, которые были изменены с момента последней загрузки.

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

2

Это действительно проблема, если ваш веб-сервер устанавливает длинный заголовок «Expires» (установка чего-то вроде ExpiresDefault "access plus 10 years" в вашей конфигурации Apache). В противном случае браузер сделает условное GET, основанное на измененном времени и/или Etag. Вы можете проверить, что происходит на вашем сайте, с помощью веб-прокси или расширения, такого как Firebug (на панели Net). В вашем вопросе не упоминается, как настроен ваш веб-сервер, и какие заголовки он отправляет со статическими файлами.

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

1

Я считаю, что сочетание решений лучше всего работает:

  1. Настройка кэша даты истечения срока действия для каждого типа ресурсов (изображения, страницы и т.д.) appropreatly для этого ресурса, например:

    • Ваши статические страницы «О себе», «Контакт» и т. Д., Вероятно, не будут меняться больше, чем несколько раз в год, поэтому вы можете легко положить время кеша в месяц на этих страницах.
    • Изображения, используемые в этих страницах, могут иметь вечное время кеширования, так как вы более похожи, чтобы заменить изображение, а затем изменить его.
    • Аватары могут иметь срок годности.
  2. Некоторым ресурсам требуются измененные даты в их именах. Например, аватары, сгенерированные изображения и тому подобное.

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

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

1

Возможно, вы захотите проверить подход, сделанный плагином «uiperformance», который вы можете найти here. Он делает много вещей, о которых вы упоминаете, но автоматизирует их (время истечения срока действия в течение длительного времени, а затем увеличивает номера версий при изменении файлов).

Итак, если вы используете грааль, вы получаете это бесплатно.Если вы этого не сделаете - возможно, вы сможете одолжить применяемые методы.

Также - заимствованная страница ui-performance page, - читайте следующее 14 rules.

1

ETags, казалось бы обеспечить решение для этого ...

По http://httpd.apache.org/docs/2.0/mod/core.html#fileetag, можно настроить браузер для создания ETags по размеру файла (вместо времени/инод/и т.д.). Это поколение должно быть постоянным при развертывании нескольких серверов.

Просто включите его в (/etc/apache2/apache2.conf)

FileETag Size 

& вы должны быть хорошо!

Таким образом, вы можете просто ссылаться на свои образы как <img src='/path/to/foo.png' /> и по-прежнему использовать все преимущества кеширования HTTP.