9 апреля 2010 в 12:40

Правильное REST кэширование

Пусть мы хотим написать свой хабрахабр с блекджеком и прочими прелестями. Страница статьи у нас стостоит из 3 объёмных блоков:
1. собственно текст статьи. меняется очень редко.
2. дерево комментариев. меняется относительно часто, но со временем всё реже и реже.
3. прямой эфир. небольшой, но меняется очень часто.

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

?article:right.cache/content/version:123
?article:right.cache/comments/time:2010-12-01
?live/time:2010-12-01
?style:article/version:666
?script:article/version:333

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

Такая организация гарантирует нам, что при появлении новых комментариев не придётся грузить статью заново. И наоборот, при изменении статьи не надо будет перегружать всё дерево комментариев. А уж про то, что из-за часто меняющегося прямого эфира нам не надо по новой грузить весь контент, и заикаться не стоит ;-)

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

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

Индекс не представляет из себя ничего нового.

<!DOCTYPE t:stylesheet [ <!ATTLIST t:stylesheet id ID #REQUIRED> ]><br><?xml-stylesheet type="text/xsl" href="#t:stylesheet"?><br><t:stylesheet id="t:stylesheet" version="1.0" xmlns:t="http://www.w3.org/1999/XSL/Transform"><br>    <t:output doctype-public="-//W3C//DTD XHTML 2.0//EN" doctype-system="http://www.w3.org/MarkUp/DTD/xhtml2.dtd" /><br>    <t:template match=" @* | node() "><br>        <t:copy><br>            <t:apply-templates select=" @* | node() " /><br>        </t:copy><br>    </t:template><br>    <t:template match=" *[ @src and contains( @srctype, 'xml' ) ] "><br>        <t:copy><br>            <t:apply-templates select=" @* " /><br>            <t:apply-templates select=' document( @src )//html/body/* ' /><br>        </t:copy><br>    </t:template><br>    <t:template match=" / "><br>        <t:apply-templates select=" document( '#t:stylesheet' )//html " /><br>    </t:template><br>    <t:template name="content"><br>        <html><br>         <head><br>            <title>Article with comments</title><br>            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><br>            <link href="?style:article/version:666" type="text/css" rel="stylesheet" /><br>            <script src="?script:article/version:333" type="text/javascript"></script><br>         </head><br>         <body><br>             <section src="?article:right.cache/content/version:123" srctype="text/xml"><br>                 <a href="?article:right.cache/content/version:123">article content</a><br>             </section><br>             <section src="?article:right.cache/comments/time:2010-12-01" srctype="text/xml"><br>                 <a href="?article:right.cache/comments/time:2010-12-01">article comments</a><br>             </section><br>             <section src="?live/time:2010-12-01" srctype="text/xml"><br>                 <a href="?live/time:2010-12-01">live content</a><br>             </section><br>         </body><br>        </html><br>    </t:template><br></t:stylesheet>


А вот у ресурса будут другие шаблоны.

<!DOCTYPE t:stylesheet [ <!ATTLIST t:stylesheet id ID #REQUIRED> ]><br><?xml-stylesheet type="text/xsl" href="#t:stylesheet"?><br><t:stylesheet id="t:stylesheet" version="1.0" xmlns:t="http://www.w3.org/1999/XSL/Transform"><br>    <t:output doctype-public="-//W3C//DTD XHTML 2.0//EN" doctype-system="http://www.w3.org/MarkUp/DTD/xhtml2.dtd" /><br>    <t:template name="content"><br>        <html><br>            <head><br>                <title>Article content</title><br>                <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><br>            </head><br>            <body><br>                <h>Article content</h><br>            </body><br>        </html><br>    </t:template><br>    <t:template match=" / "><br>        <t:element name="meta"><br>            <t:attribute name="http-equiv">refresh</attribute><br>            <t:attribute name="content">0;url=?article:right.cache</attribute><br>        </t:element><br>    </t:template><br></t:stylesheet>

При прямом запросе браузером произойдёт редирект на ?article:right.cache
Поисковик же получит контент и проиндексирует его.
​Василий Пупкинъ @tenshi
карма
–1,0
рейтинг 0,0
Похожие публикации
Самое читаемое Разработка

Комментарии (43)

  • +3
    Спасибо за статью.

    >>Поисковик же получит контент и проиндексирует его.
    Но не так, как было бы с обычной страницей. Нужно исследование как это влияет на выдачу.
    • 0
      конечно, но это к сожалению не мой профиль ._.
    • +1
      Про этот вариант ничего не скажу — не использовал такое. А обычный xHTML+XSLT роботы кушают хорошо. И даже лучше чем обычные страницы. Потому что XSLT позволяет реализовать 100% и валидный noindex.
      • +1
        кастомный доктайп тоже это может ;-)
        • +2
          Я не знаю каким образом поисковики определяют что есть HTML, а что XSLT и насколько важно для них то, что они определили. Вполне допускаю, что есть какие-нибудь нюансы.
          Я вообще очень удивился, когда обнаружил, что goolge XSLT-файл из xml-stylesheet сожрал и проиндексировал. Яндекс, естественно, его проигнорировал.
          • 0
            покажи его о_0"
            • +2
              Задача была именно что скрыть XSLT.
              Я делаю через него include того, что робот не должен видеть. Но фокус не удался.
              • 0
                а точно делаешь? просто я пооткрывал твои сайты в хроме и не нашёл там упоминания об xslt
                • +3
                  Фича такая. Хром в исходниках дает не исходник, а результат преобразования.
                  Я когда эти сайты делал хрома не было. На одном из них вообще в хроме подвал перекошен.
                  У меня есть глубокомысленный личный проект на нескольких доменах с 15.000 посещаемостью и почти 10 летней историей. Модернизация давно назрела. Потихоньку делаю. Вот все думаю сделать — не сделать на XSLT-клиент.
                  Хочется, но больно геморойно и никак не справлюсь с DOCTYPE.
                  Декларативно все генерируют правильно. По факту — квиркмод
                  • +1
                    а что там сложного с доктайпом?
                    • +1
                      Парсится все как будто квиркмод выставлен, а не xHTML, который я задал.
                      • 0
                        покажи код
  • +2
    При прямом запросе браузером произойдёт редирект на ?article:right.cache
    Поисковик же получит контент и проиндексирует его.

    Я конечно не большой специалист в SEO, но разве это не будет считаться клоакингом, со всеми вытекающими?
    • +2
      нет
      • +2
        Эт хорошо. Хоть и не мой профиль, но статья интересная, спасибо.
    • +2
      Это не более чем технический прием. Если в с его помощью будете спамить и клоачить — это будет считаться спамом и клоакингом, если нет — не будет.
      Но в отличие от традиционных способов клоакинга — этот поисковики опредить не могут. Только по стуку.
  • +1
    вопрос на засыпку: при чем здесь клиентская оптимизация? :)
    • +2
      да, ответ глубоко зарыт
      habrahabr.ru/blogs/xslt/90373/#comment_2720218

      имхо, технология интересная, но тупиковая: размер HTML сейчас нисколько не критичен (после автоматического gzip/minify), а вот скорость разбора документа на клиенте весьма и весьма важна…
      • 0
        не знаю насколько он не критичен, но при обновлении страницы со статьёй на хабре я вижу как она очень постепенно загружается в течении 3 секунд на 10 мегабитах.
        • +2
          я, конечно, извиняюсь, но загружается она так медленно из-за
          (1) большого количества рекламных вызовов «там-сям»
          (2) из-за бардака в клиентской оптимизации (количество файлов легко можно вдвое уменьшить)

          Данный подход конкретно для Хабра сделает только хуже
      • 0
        >размер HTML сейчас нисколько не критичен
        Ну да у всех же безлимит 100Мбит.
        >а вот скорость разбора документа на клиенте весьма и весьма важна…
        Не замерял. Честно признаюсь. Хотя надо было бы.
        Визуально (если не парсить все дерево, а только переставлять и подтягивать блоки) все выполняется достаточно быстро, задержка незаметна. Что достаточно важно — все блоки появляются одновременно, а не как с include через Ajax. Даже если по времени это было бы дольше, чисто психологически — задержка не так заметна как в случае с постепенным появлением контента.

        • +1
          >>размер HTML сейчас нисколько не критичен
          > Ну да у всех же безлимит 100Мбит.

          Извините, но Вы сейчас чушь сказали :) Посчитайте, сколько пользователь теряет
          1) На загрузке сжатого и архивированного HTML
          2) На загрузке всех остальных (пусть и оптимизированных) компонентов страницы
          3) На осуществлении 3-5 дополнительных запросов к серверу, которые не уменьшают размер HTML

          И лучше это делать для пинга не в 1 мс :) Потому что в этом случае у пользователей, скорее всего, 100Мбит

        • +1
          > Не замерял. Честно признаюсь. Хотя надо было бы.

          Замерьте. И не на двухядерном Xeon, а на каком-нибудь нетбуке. Все будет куда менее очевидно :)

          Хотя интереснее это будет сравнить со скоростью загрузки этих же элементов в iframe. Мне кажется, что разброс будет в районе 10-20%.
          • 0
            Смысл применения этой технологии в том, чтобы один раз скачанный блок кешировался и больше не грузился. Т.е. Мы теряем при первой загрузке страницы, зато при повторных мы выигрываем.
            Насчёт разницы в скорости отрисовки спорить без тестов, и тем более приводить какие то числа-немного странно, имхо. Но могу сказать что у xslt неплохие шансы, потому что эту технологию разрабатывает w3c и она в плане быстродействия очень неплоха.
            • +1
              имхо
              1) Реализация обновления страницы на уровне браузера убивает, как минимум, 50% выигрыша. Все «сэнономленные» запросы будут вновь и вновь уходить-приходить (новая вкладка, Ctrl+R, что-то еще браузеру не понравится). Я не вижу здесь какой «прорывной» составляющей: еще один (скорее всего) спорный подход к решению давней проблемы.
              2) xslt тормозит. Везде: на клиенте и на сервере. Это аксиома такая :)
              3) Говорить о том, что w3c разрабатывает xslt, и поэтому эта технология крута, — глупо. XHTML2 тоже w3c разрабатывает (или уже таки остановилось?), но это явно тупиковая ветвь. Таких полутупиковых (потому что они не мейнстрим, и часто применяются не по делу) технологий у w3c много (было бы странно, если бы такая крупная организация разрабатывала исключительно только мейнстрим :)

              и, наконец,
              4) экономить по 4-10 Кб на таких запросах (эта страница в сжатом виде (даже без minify) меньше 20 Кб занимает) — это уже совсем перекос. Лучше блокирующие скрипты пересобрать/вынести и стили нормальные написать (чтобы рендеринг не тормозил) — эффекта будет больше. Или картинки в спрайты / data:URI запихнуть. Или их поотимизировать. Или выкинуть лишнюю клиентскую логику (типа инициализации jQuery везде, где только можно).
              • –1
                1. именно для этого и используется жёсткое кэширование ресурсов.
                2. глупость.
                4. 20кб через жопорез — это не так уж и мало.
              • +1
                2) xslt тормозит. Везде: на клиенте и на сервере. Это аксиома такая :)
                На клиенте я не знаю не замерял. А на сервере скажу — операция финишной сборки (у меня) занимает 2-5 мсек. На локальном серевере (слабенький ноутбук) 5-10 мсек.
                Я понимаю, что у броузеров парсеры сильно-сильно тормозные, но не настолько же.
                4) Если верить данным li.ru на среднем новостном сайте соотношение хитов/хостам 1:10
                10K*10 хитов = 100

                Я с вами согласен в п.2 клиентский XSLT, скорее всего тупиковая вещь. За десять лет я не вижу никаких подвижек в эту строну. И вряд ли увижу дальше.

                • 0
                  в браузерах совсем не тормозные: libxslt, msxml, transformiix
  • +1
    Статья — понятно. Комменты — понятно. А что за «прямой эфир»?
    • +1
      справа от этой статьи посмотрите :)
      • +2
        Справа от статьи у болшой прямоугольник, в котором написано «click here to download plugin» :-)
        • +1
          • +2
            Во оно как, оказывается я.директ еще там висит, вот так на чужих скринах и узнаешь как должна выглядить страница без адблокеров )))
  • 0
    Интересная идея, но заморочная имхо, и вообще непривычны все эти инклюды, наверно лучше внутри сервера все же собирать страницу из (возможно) кешированных кусочков. Идея с использованием номера версии/даты обновления интересная.
    • 0
      Тут смысл в клиентском кешировании больших блоков.
      Например, у вас на сайте есть объемный сайд бар (или большое левое меню как на alibaba.com)
      В таком случае не имеет смысла заставлять пользователя каждый раз его перекачивать, проще 1 раз ему отдать этот сайдбар чтобы он закешировался.

      Идея с использованием ревизии/даты обновления довольно стандартная и часто используется для кеширования статики (изображений/цсс).

      Работает она так: например у вас есть картинка сайт.ру/img/image.jpg
      вы пишете рерайт правилос чтобы site.ru/img/r[0-9]*/image.jpg вело на /img/image.jpg, а саму image.jpg отдаете с сервера с заголовком expires на год вперед чтобы она у клиента закешировалась хорошо и надолго.
      теперь при изменении изображения вам придется переписать ссылку на него везде где она есть на ссылку с новой ревизией (обычно это делается автоматически с помощью какой нибудь функции Template::getImageUrl('image.jpg'); которая проверяет ревизию файла и вставляет нужную ссылку), а у клиента соответсвенно браузер видит что изменился урл и перекачивает картинку, которую потом снова кеширует. Такой подход сопряжен с определенными сложностями — в частности, придется переписывать весь цсс чтобы там картинки вставлялись с помощью вашей функции.

      Есть еще одна хитрость, которая позволяет облегчить работу с цсс — можно создавать домены третьего уровня вида r[0-9]*.site.ru, который будет просто альясом основного сайта (настраивается в dns один раз, не надо каждый раз создавать поддомены), и в шапке инклудится какой нибудь master.css с домена r123.site.ru, который уже подтягивает все другие css.
      Смысл такого подхода в том, что другие цсс и картинки будут тянуться с того же домена, что и мастер.цсс, и следовательно для изменения ревизии достаточно в 1 месте — в шапке поменять домен и на клиентской машине обновится весь кеш разом.

      Комбинирование этих двух подходов (поддомен для css и Rewrite для статики, которая вставляется на странице) позволяет хорошо кешировать статику сайта на клиентской машине, и с точки зрения разработчика этот кеш удобно обновлять.
      • +1
        как раз загрузка (конкретно) css с таких доменов — шаблон антиоптимизации. Ключевое слово — DNS.
        Лучше просто реврайты, типа
        main.wo123.css -> main.css
        • 0
          зачем нам лишние обращения к днс? лучше уж так: css.xxx.ru/123.css
          • +1
            css.xxx.ru — еще один шаблон антиоптимизации. DNS-запрос в первый раз не кэшируется, а потом он не нужен, потому что CSS закэширован :) В итоге каждый раз мы (если только CSS не раз в час меняется :) делаем доп. DNS-запрос для CSS-файла.
            • 0
              у тебя всё-равно будут лишние запросы к днс после каждого апдейта вёрстки
              • +1
                не будут, если CSS на том же домене. 500 байтов cookie — ничто по сравнению с 1 DNS-запросом.
                • +1
                  а, блин… я подумал, что main.wo123.css — это домен такой у тебя х)
            • +1
              Просто реврайты такого типа добавляют сложности с тем, что надо искать все вхождения картинок в цсс и им прописывать новую ревизию, хотя это конечно можно автоматизировать каким нибудь скриптоп перед выкладыванием новой верстки на продакш. Способ с поддоменами хорош тем, что легко внедряется и требует относительно мало трудозатрат, при этом свою задачу выполняет исправно-кеширует цсс и картинки из цсс и позволяет быстро обновить всю верстку.
              Опять же, вынос изображений и цсс на поддомен плох тем, что создает лишний днс запрос, а хорош тем, что позволяет создать больше параллельных запросов с сервера ( актуально например в случае новостных лент с обилием изображений)
              • +1
                В WEBO Site SpeedUp это уже давно автоматизировано и работает на нескольких тысячах сайтов.

                CSS на отдельный поддомен выносить — зло! расстреливать за это нужно.

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