Оптимизация загрузки страниц на практике

    Наткнулся на кучу интересных статей на webo.in и зачитался. Решил применить описанное там на реальном проекте. Вот что получилось. Проектик маленький — сайт моих друзей Bookcare. Они делают обложки для книг, а их сайт — мой «проект выходного дня».
    Теперь по сути:
    Основная часть контента — это изображения обложек, которые хранятся в формате jpeg. Значит действия над этой частью оптимизации сводятся к оптимизации самих картинок и настройке кеширования.
    Для оптимизации картинок я использовал jpegtran, однако проблема с ним была в том, что он не может обработать сразу всю папку — только по одному файлу. Другая проблема, что использовать входной файл в качестве выходного (просто перезаписать оптимизированный файл в старый) невозможно.
    Я решил эту проблему небольшим скриптиком и, хотя я не очень силён в шелл-скриптах, он работает и достаточно удобно производить замены. Итак, я создал в домашней папке скрипт jp.sh:

    jpegtran -copy none -optimize -perfect "$1" > "оsh_conv_$1";\
    mv "$1" raw/
    mv "оsh_conv_$1" "$1"

    А затем выполнял простую gоследовательность:
    # mkdir raw
    # find *.jpg -depth 0 -type f -iregex ".*\.jpg" -exec ~/s.sh {} \;

    После этого в папке с картинками уже лежат оптимизированные джепеги, и создаётся новая папка raw/, содержащая исходные файлы. Папочка raw/ очень пригодилась, когда оказалось, что скриптик мой не отрабатывал на файлах с именами, содержамищи не латиницу (файлы просто обнулялись). Сейчас всё работает.

    Итак, после того, как картинки удобным образом оптимизированы, надо было настроить кеширование. Я добавил выдачу Etag'ов. Это просто и описано на webo.in. Делается добавлением одной строки в htaccess

    FileETag MTime Size

    Вообще-то надо еще настроить выдачу заголовков Expires, но это тоже делается несложно.

    После разборок с каринками, они стали весить на 34% меньше, что на странице Каталога ускорило загрузку почти на столько же, так как основной контент — это именно картинки.

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

    Далее интересней. Захотелось, чтобы все (даже пхпшные) странички выдавали LastModified и правильно откликались на If-Modified-Since.
    Взглянул на главную страницу и понял, что там выводятся только новости и последние поступления (из динамического контента). Соответственно получил дату последней новости, дату последнего поступления, получил максимальную и обработал следующим образом:

    // $md - Штамп даты последнего изменения
    $modified = gmdate("D, d M Y H:i:s",$md)." GMT";// Приводим к HTTPшному формату - "Mon, 20 Dec 2004 09:34:19 GMT";
    $hdr = '';

    $headers = apache_request_headers();

    foreach ($headers as $header => $value) {
    if (eregi('If-Modified-Since', $header)) {$hdr = $value;}
    }

    if ($hdr === $modified) {
    header ("HTTP/1.1 304 Not Modified ");
    header ("Last-Modified: ". $modified);
    header ("Expires:");
    header ("Cache-Control:");
    exit();
    }
    header ("Last-Modified: $modified");
    header ("Expires:"); // обнуляем ненужные заголовки
    header ("Cache-Control:");


    * This source code was highlighted with Source Code Highlighter.


    Надо отметить, что время в Last-Modified надо указывать всегда в GMT, то есть по гринвичу.

    Теперь страничка загружается один раз, а далее мы видим ответ 304 Not Modified;
    Еще одно важное замечание — заголовок Last-Modified надо выдавать всегда, иначе страница будет загружаться через раз, так как в ответе 304 не было указано Last-Modified. Такое поведение было замечено за FF3.

    Затем добрался до выдачи css в виде архивов. Тут меня не устроило решение, которое было дано на webo.in, так как на серваке bookcare.ru нет mod_headers. Я использовал достаточно спорный метод, но в рамках данной задачи, я считаю, вполне законный:

    RewriteEngine On
    AddEncoding gzip .css
    RewriteCond %{HTTP:Accept-encoding} !gzip [OR]
    RewriteCond %{HTTP_USER_AGENT} Konqueror
    RewriteRule ^(.*)\.css$ $1_css.nozip [L]

    Соответственно, например main.css у нас изначально лежит в виде архива, а main_css.nozip — это обычная версия файла.
    Если эти строки лежат в .htaccess в отдельной папке, то никак не влияют на файлы вне этой папки, так что в таком виде этот приём только полезен. При минимальных действиях на сервере получается то, что должно получаться.

    Вот, наверное всё, если я чего-нибудь не запамятовал.

    UPD1: Всё-таки запамятовал. После вырезания всех лишних символов из хтмл-выдачи отдаётся она в gzipованом виде с коэффициентом сжатия 9.

    UPD2: Переписал шелловый скрипт. Теперь всё работает хорошо и с любыми именами

    UPD3: Обнаружилась проблема с выдачей css в виде архивов. Исправил код .htaccess

    UPD4: По совету юзера Roxis публикую код на пхп, который выбаёт правильные заголовки без привязки к mod_php

    $hdr = isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])?$_SERVER['HTTP_IF_MODIFIED_SINCE']:'' ;

    if ($hdr === $modified) {
     header ("HTTP/1.1 304 Not Modified ");
     header ("Last-Modified: $modified");
     header ("Expires:");
     header ("Cache-Control:");
     exit();
    }
    header ("Last-Modified: $modified");
    header ("Expires:");
    header ("Cache-Control:");


    * This source code was highlighted with Source Code Highlighter.


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

    Подробнее
    Реклама
    Комментарии 46
    • 0
      а можно кат?
      • +3
        дадада. простите великодушно =)
        • 0
          Того, кто просит убрать под кат всегда минусуют. Но кто то же должен подсказать это автору? Зачем минусовать.
          • 0
            а ещё обычно есть человек, который говорит, что «того, кто просит убрать под кат всегда минусуют»

            будем продолжать цепочку или уже наконец-то сделают автокат?
        • +5
          A.
          $m = explode(' ', $d2);// $d2 - дата в формате MySQL - "YYYY-mm-dd hh:mm:ss"

          $m['d'] = explode('-',$m[0]);// Разбиваем, чтобы потом получить штамп
          $m['t'] = explode(':',$m[1]);

          $md = mktime($m['t'][0],$m['t'][1],$m['t'][2],$m['d'][1],$m['d'][2],$m['d'][0]);


          Вот так делать — это глупость.

          1. Не будущее — лучше даже в mysql-ой базе хранить данные в виде unix timestamp. Т.е. обычным числом, тогда не придется так изворачиваться.
          2. Даже если вы храните в дате, в выборке можно спокойно указать SELECT UNIX_TIMESTAMP(date); и получить на выходе готовый.
          3. Даже если приходится хочешь-нехочешь а обрабатывать время в виде mysql-формате, то для этого существует php-функция strtotime.
          4. А вообще, если делать по нормальному надо прямо в SELECT-формировать дату в нужном формате. Это спокойно делается, посмотрите ф-ии mysql для работы со временем.

          Б. Даже в HTML выдаче лчше делать на основе E-tag, который может вычисляться как md5 или sha1 от времени последнего изменения страницы. Результат проще, но мороки меньше.

          В. О eregi уже давно пора немного забыть.
          • +3
            Согласен со всем. Поправлю.
          • +1
            1. последний вариант не будет работать корректно без mod_headers, лучше уже тогда
            AddOutputFilterByType DEFLATE text/html
            AddOutputFilterByType DEFLATE text/css
            AddOutputFilterByType DEFLATE text/javascript
            AddOutputFilterByType DEFLATE application/x-javascript
            — сам недавно так на одном виртуальном хостинге подрубал

            либо так, как описано в самом начале неуказанной статью по сжатие:
            AddEncoding gzip .gz
            RewriteCond %{HTTP:Accept-encoding} gzip
            RewriteCond %{REQUEST_FILENAME}.gz -f
            RewriteRule ^(.*)$ $1.gz [QSA,L]

            2. Про gzip и альтернативные хосты ни слова — они применялись? Хотя, на странице, в среднем, не больше 5 картинок, так что особо и не требуется…

            3. Да, и можно эту статью на webo.in перепечатать? :)
            • +1
              1. Ну я не указал конкретно статью, просто сослася на сам сайт =)

              2. Альтернативные хосты не применял, но, собираюсь, ибо сейчас сайт в зачаточном состоянии, а вообще — там основная задача — показывать товар лицом. например страница Каталога вообще только и содержит, что картинки/

              3. почту за честь.
              • 0
                про gzip не ответили :) Приведите его тоже в статье, так как для HTML он точно как-то включен :)
          • +1
            А результат-то такой? Какое было время загрузки, на сколько уменьшилось? Судя по сайту, он и неоптимизированный вряд ли тормозил – стоила ли игра свеч?
            • +1
              Картинки стали весить меньше на 30%. На странице каталога это было очень критично, да и кеширование свело время загрузки (для не новых пользователей) на нет.
              • +1
                А Last-Modified помогает не только юзерам, но и поисковым роботам.
              • 0
                Ну а в среднем загрузка страницы насколько стала быстрее? Замеры проверяли?
                • +1
                  без привязки к mod_php: $_SERVER['HTTP_IF_MODIFIED_SINCE']
                  • 0
                    спасибо
                    • 0
                      Добавил исправленный с Вашей помощью код =)
                      • 0
                        ещё проверку isset() желательно добавить
                  • +1
                    "… тдаётся она в gzipованом виде с коэффициентом сжатия 9.."
                    Сожмет чуть лучше чем на 1...3, а процессора поест в разы больше

                    • 0
                      Этот вопрос я еще не изучал пристально. В ближайшее время поиграюсь разными значениями, потестирую. Если получится что-то дельное — напишу.
                      • 0
                        Народ давно уже поигрался и графики нарисовал, ищите в ПС
                        • 0
                          Эти тесты — вещь сильно зависящая от среды, так что новые графики для сравнения будут не лишними.
                    • +1
                      Насчет файлов с кириллицей — это проблемы с начтройкой сервера, такого не должно быть. И в баш-скрипте *всегда* берите имена файлов в кавычки, "$1" вместо $1, иначе будут глюки с файлами с пробелами и спецсимволами.
                      • 0
                        Спасибо, буду иметь в виду. Видимо из-за этого и были проблемы. Сейчас проверю.
                      • +1
                        для оптимизации и чистки html кода могу посоветовать использовать tidy, www.w3.org/People/Raggett/tidy/
                        • 0
                          Ну тут не совсем такая оптимизация нужна. Например, в одном из методов уменьшения размера html-кода предлагается не закрывать некоторые теги. Тут же просто приводится к «идеальному» виду, например:
                          heading
                          <h2>subheading

                          is mapped to

                          heading
                          subheading

                          Тут размер увеличивается.
                          • 0
                            <h1>heading
                            <h2>subheading</h3>

                            is mapped to

                            <h1>heading</h1>
                            <h2>subheading</h2>

                            Простите, теги не поправил
                            • 0
                              тиди не только это умеет делать, не внимательно читали
                      • +2
                        А я думал, че за глюки в ФФ3 иногда…
                        • +1
                          И на сколько сократился трафик? Какие результаты в целом? Или пока рановато мерить?
                          • +2
                            Спасибо, интересно.

                            Опечатка в третьей строке примера ($hrd=''), было бы хорошо исправить на случай копи-паста.

                            — BookCare — это отлично! Отчаянно хочу обложку :)
                            • НЛО прилетело и опубликовало эту надпись здесь
                              • –1
                                Ошибаетесь милейший, это не микрооптимизация сайтов визиток, а привлечение траффика на говносайт.
                                • НЛО прилетело и опубликовало эту надпись здесь
                                  • 0
                                    > с этим я полностью согласен
                                    Ваше право
                                  • 0
                                    > привлечение траффика
                                    Вот это вообще ноу комментс. Айтишники с электорнными книгами на своих сони ридерах — самая целевая аудитория для производителей обложек книг.

                                    > на говносайт
                                    ХСПВ, милейший.
                                  • 0
                                    > микрооптимизация сайтов визиток — конечно ахуенная тема. бред.
                                    Ну уж простите, не у каждого есть под рукой яндекс. За неимением большего, попробовал воплотить методы на малом сайте.
                                    Представленные методы без труда можно применить и на большом портале.

                                    > ТС найди норм работу раз умный
                                    Позвольте мне решать самому, что делать.
                                  • 0
                                    > Проектик маленький — сайт моих друзей Bookcare.

                                    Да что ж такое… Проектик маленький, а раздел «Зачем мне обложка?» существует только в виде пункта меню.
                                    • 0
                                      Спасибо за сигнал, товарешч =)
                                    • +1
                                      # find *.jpg -depth 0 -type f -iregex ".*\.jpg" -exec ~/s.sh {} \;

                                      Есть способ проще:

                                      # for i in *.jpg; do ~/s.sh "${i}"; done
                                    • 0
                                      >>>… иначе страница будет загружаться через раз, так как в ответе 304 не было указано Last-Modified. Такое поведение было замечено за FF3.

                                      Я столкнулся с подобной проблемой. Но у меня это отражается на картинках. Вкратце, я делаю поддержку тем для веб-приложения. Чтобы иметь возможность переключать тему «на лету» я добавил опции кэширования. Пока тема прежняя сервер для всех картинок и css отвечает 304, как только тема меняется, серве отвечает 200 меняя last modified timestamp и etag. Плюс я использую «Cache-Control»: «public, must-revalidate». Работает в Chrome, IE, Safari. В FF 3 работает «частично». Проблема в следующем. В приложении есть Табы, которые состоят из 3-х картинок — левая часть, правая часть, бэкграунд. Так вот Табов больше одного, соответственно одинаковых ссылок на одни и те же картинки столько же сколько и Табов. Так вот FF 3 обновляет картинки Табов не для всех Табов, причем LiveHTTPHeaders и Fiddler показывают что сервер работает правильно — отдает «свежую» картинку. Но FF 3 обновляет не все Табы :(( Такое ощущение что есть проблема в синхронизации. Например, FF одновременно шлет несколько запросов на одну и ту же картинку. Первый реквест получает 200 респонс. Следующий реквет уже получает 304 респонс. Первая картинка обновляется из респонса и кладется в локальны или ин-мемори кэш FF, но при этом успевает вернуться второй ответ с кодом 304, который лезет в кэш и берет еще не обновившуюся картинку %). Если кликнуть по любой ссылке или рефрешнуть страничку, то все обновится правильно, потому что в кэше уже будут лежать «свежие» картинки.

                                      Помогите кто может советом, очень хочется побороть эту траблу?
                                      • 0
                                        Ну можно попробовать картинки не в верстке прописывать, а в css. Тогда загрузка должна быть одна (для одного свойства).
                                        • 0
                                          Что я хочу понять — это баг в FF или есть решение? :) Мне кажется, что Вы правы насчет CSS — в крайнем случае сделаю именно так. Спасибо!

                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.