Кэшированные, сгенерированные PHP миниатюры загружаются медленно
Вопрос Часть A ▉ (100 наград, награжден)
Главным вопросом было, как сделать этот сайт, быстрее загружаться. Сначала нам нужно было прочитать эти водопады. Спасибо всем за ваши предложения по анализу показаний водопада. Из приведенных здесь графиков различных водопадов видно главное узкое место: миниатюры, генерируемые PHP. Безрецептурная загрузка jquery из CDN по совету Дэвида получила мою награду, хотя в целом мой сайт был только на 3% быстрее, и при этом не отвечал на основное узкое место сайта. Время для прояснения моего вопроса и еще одной награды:
Вопрос Часть B ▉ (100 наград, присуждено)
Новый фокус теперь должен был решить проблему, которая была у 6 изображений jpg, которые вызывают большую часть задержки загрузки. Эти 6 изображений представляют собой миниатюры, сгенерированные PHP, крошечные и всего 3~5 КБ, но загружаются относительно очень медленно. Обратите внимание на "время первого байта" на различных графиках. Проблема осталась нерешенной, но награда досталась Джеймсу, который исправил ошибку заголовка, которую RedBot подчеркнул: "Условный запрос If-Modified-Since вернул весь контент без изменений".,
Вопрос Часть C ▉ (моя последняя награда: 250 баллов)
К сожалению, после исправления даже ошибки заголовка REdbot.org задержка, вызванная сгенерированными PHP изображениями, осталась нетронутой. О чем думают эти крошечные миниатюрные миниатюры размером 3~5Кб? Вся эта информация заголовка может послать ракету на Луну и обратно. Любые предложения по поводу этого узкого места очень ценятся и рассматриваются как возможный ответ, так как я застрял в этой узкой проблеме уже семь месяцев. Заранее спасибо
[Некоторая справочная информация на моем сайте: CSS вверху. JS внизу (Jquery, пользовательский интерфейс JQuery, купленные движки меню awm/menu.js, движок вкладок js, видео swfobject.js) Черные линии на втором изображении показывают, что следует загружать. Злой робот - мой питомец "ZAM". Он безвреден и часто счастливее.]
Загрузить Водопад: Хронологический | http://webpagetest.org/
Параллельные домены сгруппированы | http://webpagetest.org/
Сайт-Перф Водопад | http://site-perf.com/
Pingdom Инструменты Водопад | http://tools.pingdom.com/
GTmetrix Водопад | http://gtmetrix.com/
19 ответов
Во-первых, использование этих нескольких доменов требует нескольких поисков DNS. Лучше было бы объединить многие из этих изображений в спрайт, а не распространять запросы.
Во-вторых, когда я загружаю вашу страницу, я вижу большую часть блокировок (~1.25 с) на all.js. Я вижу, что начинается с (старая версия) jQuery. Вы должны ссылаться на это из Google CDN, чтобы не только уменьшить время загрузки, но и потенциально полностью избежать HTTP-запроса.
В частности, по этим URL можно ссылаться на самые последние библиотеки пользовательского интерфейса jQuery и jQuery (см. Этот пост, если вам интересно, почему я пропустил http:
):
//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js
//ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js
Если вы используете одну из стандартных тем пользовательского интерфейса jQuery, вы также можете извлечь ее CSS и изображения из Google CDN.
Оптимизировав хостинг jQuery, вы также должны комбинировать awmlib2.js
а также tooltiplib.js
в один файл.
Если вы решите эти вопросы, вы увидите значительное улучшение.
У меня была похожая проблема несколько дней назад, и я нашел head.js. Это плагин Javascript, который позволяет загружать все файлы JS paralell. Надеюсь, это поможет.
Я далеко не эксперт, но...
В связи с этим: "Условный запрос If-Modified-Since вернул весь контент без изменений". и мои комментарии.
Код, используемый для генерации миниатюр, должен проверять следующее:
- Есть ли кешированная версия миниатюры.
- Является ли кэшированная версия более новой, чем исходное изображение.
Если любой из них ложен, миниатюра должна быть сгенерирована и возвращена, несмотря ни на что. Если они оба верны, то следует сделать следующую проверку:
- Есть ли заголовок HTTP_IF_MODIFIED_SINCE?
- Время последней модификации кэшированной версии совпадает с HTTP_IF_MODIFIED_SINCE
Если любой из них ложен, кэшированная миниатюра должна быть возвращена.
Если оба они верны, то должен быть возвращен статус 304 http. Я не уверен, требуется ли это, но я также лично возвращаю заголовки Cache-Control, Expires и Last-Modified вместе с 304.
Что касается GZipping, мне сообщили, что нет необходимости в GZip изображениях, поэтому игнорируйте эту часть моего комментария.
Изменить: я не заметил ваше добавление к вашему сообщению.
session_cache_limiter('public');
header("Content-type: " . $this->_mime);
header("Expires: " . gmdate("D, d M Y H:i:s", time() + 2419200) . " GMT");
// I'm sure Last-Modified should be a static value. not dynamic as you have it here.
header("Last-Modified: " . gmdate("D, d M Y H:i:s",time() - 404800000) . " GMT");
Я также уверен, что ваш код должен проверить заголовок HTTP_IF_MODIFIED_SINCE и отреагировать на него. Простая установка этих заголовков и вашего файла.htaccess не даст требуемого результата.
Я думаю, что вам нужно что-то вроде этого:
$date = 'D, d M Y H:i:s T'; // DATE_RFC850
$modified = filemtime($filename);
$expires = strtotime('1 year'); // 1 Year
header(sprintf('Cache-Control: %s, max-age=%s', 'public', $expires - time()));
header(sprintf('Expires: %s', date($date, $expires)));
header(sprintf('Last-Modified: %s', date($date, $modified)));
header(sprintf('Content-Type: %s', $mime));
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
if(strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $modified) {
header('HTTP/1.1 304 Not Modified', true, 304);
// Should have been an exit not a return. After sending the not modified http
// code, the script should end and return no content.
exit();
}
}
// Render image data
Вау, трудно объяснить вещи с помощью этого изображения... Но здесь некоторые попытки:
- файлы 33-36 загружаются так поздно, потому что они динамически загружаются в SWF, и SWF (25) загружается сначала полностью, прежде чем загружает какой-либо дополнительный контент
- файлы 20 и 21 могут быть (я не знаю, потому что я не знаю ваш код) библиотеки, которые загружаются all.js (11), но для выполнения 11, он ждет всю страницу (и ресурсы) загрузить (вы должны изменить это на domready)
- файлы 22-32 загружаются этими двумя библиотеками, опять же после того, как они полностью загружены
То есть ваш скрипт PHP генерирует миниатюры при каждой загрузке страницы? Во-первых, если изображения, которые обрабатываются с помощью большого пальца, меняются не так часто, не могли бы вы настроить кеш так, чтобы их не нужно было анализировать при каждой загрузке страницы? Во-вторых, использует ли ваш PHP-скрипт что-то вроде imagecopyresampled()
создавать эскизы? Это нетривиальный пример, и PHP-скрипт не будет ничего возвращать, пока все не будет сокращено. С помощью imagecopymerged()
вместо этого снизит качество изображения, но ускорит процесс. И сколько вы делаете сокращения? Являются ли эти миниатюры 5% размером исходного изображения или 50%? Большой размер исходного изображения, вероятно, приводит к замедлению, поскольку PHP-скрипт должен получить исходное изображение в памяти, прежде чем он сможет уменьшить его и вывести уменьшенный эскиз.
Если нет действительно веской причины (обычно ее нет), ваши изображения не должны вызывать интерпретатор PHP.
Создайте правило перезаписи для своего веб-сервера, который будет напрямую обслуживать изображение, если оно найдено в файловой системе. Если это не так, перенаправьте на ваш скрипт PHP для создания изображения. Когда вы редактируете изображение, измените имя файла изображения, чтобы заставить пользователей, которые имеют кэшированную версию, выбрать вновь отредактированное изображение.
Если это не сработает, по крайней мере, теперь, вы не будете иметь ничего общего с тем, как изображения создаются и проверяются.
Я нашел URL вашего сайта и проверил отдельный файл jpg с домашней страницы. Хотя время загрузки сейчас разумное (161 мс), оно ожидает 126 мс, что слишком много.
Все последние измененные заголовки установлены в субботу, 01 января 2011 г., 12:00:00 по Гринвичу, которая выглядит слишком "круглой" для реальной даты генерации;-)
Поскольку Cache-control - "public, max-age=14515200", произвольные последние измененные заголовки могут вызвать проблемы через 168 дней.
Во всяком случае, это не настоящая причина задержек.
Вы должны проверить, что делает ваш генератор миниатюр, когда миниатюра уже существует, и что может занять так много времени, проверяя и доставляя картинку.
Вы можете установить xdebug, чтобы профилировать сценарий и увидеть узкие места.
Может быть, все это использует фреймворк или подключается к какой-то базе данных бесплатно. Я видел очень медленный mysql_connect() на некоторых серверах, в основном потому, что они подключались с использованием TCP, а не сокетов, иногда с некоторыми проблемами DNS.
Я понимаю, что вы не можете разместить свой платный генератор здесь, но я боюсь, что есть слишком много возможных проблем...
Простое предположение, потому что этот вид анализа требует большого A/B-тестирования: кажется, что ваш домен.ch труднодоступен (длинные зеленые полосы до появления первого байта).
Это будет означать, что либо веб-сайт.ch плохо размещен, либо у вашего интернет-провайдера нет хорошего пути к ним.
Учитывая диаграммы, это может объяснить большой удар по производительности.
Кстати, есть этот крутой инструмент cuzillion, который может помочь вам разобраться с вещами в зависимости от того, какой порядок загрузки ресурсов у вас есть.
Попробуйте запустить тесты Y!Slow и Page Speed на своем сайте / странице и следуйте инструкциям, чтобы устранить возможные узкие места в производительности. Вы должны получить огромный прирост производительности, как только наберете больше очков в Y!Slow или Page Speed.
Эти тесты подскажут вам, что не так и что нужно изменить.
Это просто дикое предположение, так как я не смотрел на ваш код, но я подозреваю, что здесь могут играть роль сессии, следующее из записи PHP Manual на session_write_close()
:
Данные сеанса обычно сохраняются после того, как ваш сценарий завершается без необходимости вызывать session_write_close(), но, поскольку данные сеанса заблокированы для предотвращения одновременной записи, только один сценарий может работать с сеансом в любое время. При использовании наборов кадров вместе с сессиями вы будете испытывать загрузку кадров один за другим из-за этой блокировки.Вы можете сократить время, необходимое для загрузки всех кадров, завершив сеанс, как только все изменения переменных сеанса будут выполнены.
Как я уже сказал, я не знаю, что делает ваш код, но эти графики кажутся странно подозрительными. У меня была похожая проблема, когда я закодировал функцию обслуживания нескольких файлов, и у меня была та же проблема. При обслуживании большого файла я не мог заставить работать многокомпонентную функциональность, и не мог открыть другую страницу, пока загрузка не была завершена. призвание session_write_close()
исправил обе мои проблемы.
Исследуйте использование PHP данных сеанса. Может быть (просто возможно), генерирующий изображение PHP-скрипт ожидает блокировки данных сеанса, который блокируется главной страницей, выполняющей рендеринг, или другими сценариями рендеринга изображений. Это сделало бы все оптимизации JavaScript/ браузера практически неактуальными, так как браузер ждет сервера.
PHP блокирует данные сеанса для каждого выполняемого сценария, с момента начала обработки сеанса, до момента завершения сценария или при вызове session_write_close(). Это эффективно сериализует вещи. Проверьте страницу PHP на сессиях, особенно комментарии, как этот.
Вы пытались заменить созданные php thumnails обычными изображениями, чтобы увидеть, есть ли разница? Проблема может быть вокруг - ошибка в вашем php-коде, приводящая к регенерации миниатюры при каждом вызове сервера - задержка в вашем коде ( sleep()?), Связанная с проблемой часов - проблема жесткого диска, вызывающая очень плохое состояние гонки так как все миниатюры загружаются / генерируются одновременно.
Поскольку некоторые браузеры загружают только 2 параллельных загрузки для каждого домена, не могли бы вы добавить дополнительные домены для разделения запросов на два-три разных имени хоста. например, 1.imagecdn.com 2.imagecdn.com
Я думаю, что вместо того, чтобы использовать этот скрипт-генератор миниатюр, вы должны попробовать TinySRC для быстрого и быстрого создания миниатюр в облаке. Он имеет очень простой и удобный API, вы можете использовать как: -
http://i.tinysrc.mobi/ [высота] / [ширина] /http://domain.tld/path_to_img.jpg
[ширина] (необязательно):- это ширина в пикселях (которая переопределяет адаптивный или семейный размер). Если префикс "-" или "x", он будет вычитаться или уменьшаться до определенного процента от определенного размера.
[высота] (необязательно):- это высота в пикселях, если ширина также присутствует. Он также отменяет адаптивный или семейный размер и может иметь префикс "-" или "x".
Вы можете проверить сводку API здесь
Часто задаваемые вопросы
Сколько стоит tinySrc?
Ничего такого.
Когда я могу начать использовать tinySrc?
Сейчас.
Насколько надежен сервис?
Мы не даем никаких гарантий по поводу сервиса tinySrc. Однако он работает в крупной распределенной облачной инфраструктуре, поэтому обеспечивает высокую доступность по всему миру. Этого должно быть достаточно для всех ваших потребностей.
Как быстро это?
tinySrc кэширует изображения с измененным размером в памяти и в нашем хранилище данных на срок до 24 часов, и он не будет извлекать ваше исходное изображение каждый раз. Это делает сервис невероятно быстрым с точки зрения пользователя. (И снижает нагрузку на ваш сервер как хороший побочный эффект.)
Удачи. Просто предложение, так как вы не показываете нам код:p
Что касается отложенных миниатюр, попробуйте сделать вызов flush() сразу после последнего вызова header() в вашем скрипте генерации миниатюр. После этого восстановите график водопадов и посмотрите, есть ли задержка на теле вместо заголовков. Если это так, вам нужно внимательно взглянуть на логику, которая генерирует и / или выводит данные изображения.
Надеемся, что скрипт, который обрабатывает миниатюры, должен использовать какое-то кэширование, чтобы любые действия с изображениями, которые вы обслуживаете, выполнялись только в случае крайней необходимости. Похоже, что каждый раз, когда вы обслуживаете миниатюры, выполняется какая- то дорогостоящая операция, которая задерживает любой вывод (включая заголовки) из сценария.
Вы пытались настроить несколько поддоменов под веб-сервером NGINX специально для обслуживания статических данных, таких как изображения и таблицы стилей? Что-то полезное уже можно найти в этой теме.
Большая часть медленной проблемы - ваш TTFB (время до первого байта) слишком высок. С этим трудно справиться, не разбираясь в файлах конфигурации вашего сервера, коде и базовом оборудовании, но я вижу, что он свирепствует по каждому запросу. У вас слишком много зеленых полос (плохо) и очень маленьких синих полос (хорошо). Возможно, вы захотите немного прекратить оптимизацию внешнего интерфейса, так как я считаю, что вы многое сделали в этой области. Несмотря на то, что " 80% -90% времени отклика конечного пользователя тратится на внешний интерфейс", я полагаю, что ваше происходит в бэкэнде.
TTFB - это серверная часть, серверная часть, предварительная обработка перед выводом и рукопожатие.
Время выполнения вашего кода, чтобы найти медленные вещи, такие как медленные запросы к базе данных, время входа и выхода из функций / методов, чтобы найти медленные функции. Если вы используете php, попробуйте Firephp. Иногда это один или два медленных запроса, выполняемых во время запуска или инициализации, таких как получение информации о сеансе или проверка аутентификации, а что нет. Оптимизация запросов может привести к хорошим результатам. Иногда код запускается с использованием php prepend или spl autoload, поэтому они запускаются на всем. В других случаях это может быть неправильно настроенный Apache Conf и настройка, которая спасает день.
Ищите неэффективные петли. Ищите медленные вызовы кэшей или медленные операции ввода-вывода, вызванные неисправными дисками или большим использованием дискового пространства. Посмотрите на использование памяти и что используется и где. Запустите повторный тест веб-страницы из 10 прогонов для одного изображения или файла, используя только первый просмотр из разных мест по всему миру, а не из одного места. И читайте ваши журналы доступа и ошибок, слишком многие разработчики игнорируют их и полагаются только на выводимые на экран ошибки. Если у вашего веб-хостинга есть поддержка, обратитесь к ним за помощью, если они все равно не вежливо попросят их о помощи, это не повредит.
Вы можете попробовать DNS Prefetching для борьбы со многими доменами и ресурсами, http://html5boilerplate.com/docs/DNS-Prefetching/
Является ли ваш собственный сервер хорошим / достойным сервером? Иногда лучший сервер может решить множество проблем. Я болею за менталитет " аппаратное обеспечение дешевое, программисты - дорогое", если у вас есть возможность и деньги обновить сервер. И / или использовать CDN, такой как maxcdn, cloudflare или аналогичный.
Удачи!
(ps я не работаю ни на одну из этих компаний. Также ссылка на cloudflare выше будет доказывать, что TTFB не так важен, я добавил это, чтобы вы могли получить еще один шанс.)
Прежде всего, вам нужно справиться If-Modified-Since
запросы и такие соответственно, как сказал Джеймс. Эта ошибка гласит: "Когда я спрашиваю ваш сервер, изменено ли это изображение с прошлого раза, оно отправляет все изображение вместо простого да / нет".
Время между соединением и первым байтом - это обычно время, необходимое вашему PHP-скрипту для запуска. Очевидно, что что-то происходит, когда этот скрипт начинает работать.
- Рассматривали ли вы его профилирование? Это может иметь некоторые проблемы.
- В сочетании с вышеуказанной проблемой ваш сценарий может выполняться намного больше, чем необходимо. В идеале, он должен генерировать большие пальцы, только если исходное изображение было изменено и отправлять кэшированные большие пальцы для каждого другого запроса. Вы проверили, что скрипт генерирует изображения без необходимости (например, для каждого запроса)?
Генерировать правильные заголовки через приложение немного сложно, плюс они могут быть перезаписаны сервером. И вы подвержены злоупотреблениям, так как любой, отправляющий некоторые заголовки запроса без кэширования, заставит ваш генератор миниатюр работать непрерывно (и поднимать нагрузки). Поэтому, если возможно, попытайтесь сохранить эти сгенерированные превью, вызывайте сохраненные изображения прямо со своих страниц и управляйте заголовками из .htaccess
, В этом случае вам ничего не понадобится в вашем .htaccess
если ваш сервер настроен правильно.
Помимо этого, вы можете применить некоторые из ярких идей оптимизации из частей производительности этого общего вопроса о том, как правильно сделать сайты, например, разделить ваши ресурсы на поддоменах без файлов cookie и т. Д. Но, во всяком случае, изображение 3k загрузка не должна занимать секунды, это очевидно по сравнению с другими элементами на графиках. Вы должны попытаться определить проблему до оптимизации.
Извините, вы предоставляете немного данных. И у вас уже было несколько хороших предложений.
Как вы обслуживаете эти изображения? Если вы транслируете их через PHP, вы делаете очень плохие вещи, даже если они уже сгенерированы.
НИКОГДА НЕ ПОТОКА ИЗОБРАЖЕНИЙ С PHP. Это замедлит работу вашего сервера, независимо от способа его использования.
Поместите их в доступную папку со значимым URI. Затем позвоните им напрямую с их реальным URI. Если вам нужно генерировать на лету, вы должны поместить.htaccess в каталог images, который перенаправляет на php-скрипт генератора, только если изображение запроса отсутствует. (это называется стратегией кэширования по запросу).
Это исправит сессию php, браузер-прокси, кеширование, ETAGS, что угодно одновременно.
WP-Supercache использует эту стратегию, если она правильно настроена.
Я написал это некоторое время назад ( http://code.google.com/p/cache-on-request/source/detail?r=8), последние версии не работают, но я думаю, что 8 или меньше должны работать, и вы можете возьмем в качестве примера.htaccess просто для проверки (хотя есть и лучшие способы настроить.htaccess, чем я привык).
Я описал эту стратегию в этом посте ( http://www.stefanoforenza.com/need-for-cache/). Это, вероятно, плохо написано, но это может помочь прояснить ситуацию.
Дополнительная информация: http://meta.wikimedia.org/wiki/404_handler_caching