Пользователь
0,0
рейтинг
30 июля 2009 в 21:50

Администрирование → Кешируем блоки HTML при помощи nginx

Не секрет, что пользователи любят, когда контент на сайте обновляется чаще, чем раз в год. Эту любовь пользователей к динамическим страничкам разделяют и поисковики. Google, например, умеет определять наличие обновляющихся блоков на страничке и добавляет ей немного кармы (читай, PR).

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



Простой пример — предположим у вас был замечательный статический HTML, который отдавался nginx-ом со скоростью 2000 раз в секунду (это достижимо без особых проблем). И тут добрый дизайнер нарисовал новый вариант странички, где для залогиненных пользователей присутствует мааааленький блок, содержащий логин, имя-отчество, и, например, аватарку.

Всё, приехали. Статикой тут уже не обойдёшься, а это означает запуск PHP (Perl/Python) на каждый запрос, проверка сессии, ползание по файловой системе (или, ещё хуже, БД) для того, чтобы найти по SID логин и другую инфу о пользователе. Производительность проседает в десять раз. Или нет. :)

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

Фича номер раз, которая в нём есть с незапамятных времён — это SSI. Для того, чтобы её использовать, делаем небольшой скрипт, который умеет принимать из куки идентификатор сессии, и отдавать только тот самый небольшой блок с информацией о пользователе. То есть, на запрос вида GET /get_user_info.php отдаёт фрагмент HTML, вида <div class=«login»>Здравствуйте, Василий Пупкин</div>.

Соответственно, в самой страничке пишем SSI-include: <!--# include virtual="/get_user_info/" -->.

Для того, чтобы эта конструкция заработала, в конфиг-файле nginx надо описать соответствующий location:

location /get_user_info/ {
proxy_pass 127.0.0.1:12345/get_user_info.php;
proxy_pass_header Cookie; # для передачи sid в Cookie
}


Однако, как вы, наверное, уже заметили, подобная конструкция не решает проблемы с производительностью кода, а лишь позволяет вынести динамическую часть в отдельный скрипт. Окей, как раз для этого нужна фича номер два — кеширование прокси-запросов. В production-ветке она появилась сравнительно недавно, но уже позволяет делать очень многое. :)

Итак, для начала, скажем nginx, где мы хотим хранить наш кеш.
proxy_cache_path /var/nginx/cache
levels=1:2 keys_zone=my_cache:64m max_size=1024m
inactive=1d;


Эта строчка означает, что мы создаём новую зону для кеша, с названием my_cache, файлы кеша будут лежать в /var/nginx/cache, занимая не более чем 1 гигабайт, и храниться не более чем 1 день.

Теперь скажем nginx, что мы хотим кешировать запросы к нашей локации.

location /get_user_info/ {
proxy_pass 127.0.0.1:12345/get_user_info.php;
proxy_pass_header Cookie; # для передачи sid в Cookie

proxy_cache my_cache;
proxy_cache_valid 200 3h; # раз в три часа будем позволять себе обновлять кеш :)
proxy_cache_valid any 0; # не кешируем 500 и 400 ошибки
proxy_cache_use_stale updating error timeout invalid_header http_500 http_502 http_503 http_504 http_404; # если ваш скрипт отдал одну из описанных ошибок, использовать вариант из кеша, если он есть, даже если он уже заэкспайрился
proxy_cache_key "$scheme$proxy_host$uri$is_args$args$cookie_sid";
}


Обратите внимание на последнюю строчку. Именно она позволит нам отдавать каждому отдельному пользователю правильный вариант закешированного блока. Точнее, важен её последний параметр — переменная $cookie_sid, значение которой всегда будет равно значению куки с именем «sid», которую вам должен отдать пользователь.

Всё. Теперь nginx сам будет, по мере необходимости, обращаться к вашему скрипту, и кешировать результат его работы. При этом, производительность почти не просядет, и нагрузка на сервер вырастет не очень значительно.

Всех, кого интересуют подробности, отправляю на сайт автора nginx за документацией: sysoev.ru/nginx/docs.

UPD: Разобрался с настройками, перенёс в блог nginx.
Иван Авсеянко @Rebus
карма
146,7
рейтинг 0,0

Похожие публикации

Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Администрирование

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

  • +5
    Теперь точно поставлю nginx попробовать :)
    • +8
      Я описал только пару фич nginx, а на самом деле — тысячи их. :) Почему-то все думают, что основная ценность этого сервера в том, что он очень быстрый. А это по моему мнению не так. Самое важное — что он очень-очень гибкий.
      • –1
        Апач какбэ тоже очень гибкий. Но нгинкс быстрее :)
        • 0
          В нжинксе к тому же гибкость достигается куда меньшими трудозатратами. Всё очень лоогичноЮ удобно и превосходно документировано. В сравнении с апачем он чудо. Мне там не хватает лишь пользовательской настройки а-ля .htaccess, но всё равно это неудачное решение, по крайней мере в таком виде, как это реализовано в апаче.
          • 0
            Поясните пожалуйста про неудачное решение пользовательской настройки через .htaccess в апаче.
            • 0
              Может быть, я не иправ, но постоянно обращаться к диску за проверкой наличия .htaccess в директориях — не дело. Конечно, это вопрос правильной настройки, но на большинстве серверов он включен везде
            • 0
              Задать диапазон ip-адресов (например, стрим-москва) уже можно?
        • 0
          Апач гибкий вообще. А нгинкс гибкий как раз там где нужно для высоких нагрузок.
    • +4
      nginx это пораждение BSD-шного склада ума :)

      Он до ужаса логичен, прост и быстр. Надо было начинать прбовать уже давно ;)
      • +1
        Лень природная мешает :) Но теперь я буду с ней бороться :)
        • 0
          Из за лени и я долго не ставил — но пришлось поставить из-за того, что апач начал падать.

          Очень доволен, хотя конечно трохи с конфигами для ссл и виртуал хостов пришлось поковыряться. Работает стабильно, вопросов по памяти и процеccору никаких, изредка в топе появляется процесс.
    • –2
      Рискую нарваться на кучи минусов, но почему все в Рунете так сходят с ума по nginx'у(да и по sphinx'y)?

      Посомтрите на список Tiny Web Servers:
      en.wikipedia.org/wiki/Comparison_of_lightweight_web_servers
      • +1
        Ну лично мне мой надежный информатор советовал именно nginx. Потому это будет мой выбор :)
      • +3
        А вы попробуйте выкинуть из этого списка:

        а) Всё что closed source (цена и возможность посмотреть как оно работает);
        б) Всё, что не работает под Linux и FreeBSD (самые популярные платформы хостинга);
        в) Всё, что написано на C#, Ruby, PHP и Python (производительность);
        г) Всё что не поддерживается и не обновляется больше года (к кому идти, если нашёл баг?);
        д) Всё, что не использовалось в реальных больших и нагруженных проектах (мало ли, что автор обещает);

        И что у вас останется? Выбор из, от силы, 3-4 вариантов, среди которых nginx — один из лучших.
        • 0
          Close Source — не обязательно платное, как и Open Source — не обязательно бесплатное!
          А зачем смотреть исходники? Должно стабильно работать и выполнять поставленные задачи.

          lighttpd, чем не вариант?
          Да и возможностей поболее, например давно уже работает с FastCGI
          • +2
            nginx тоже давно fastcgi умеет
  • 0
    хорошая статья, а у меня как раз есть проект где можно оное отлично применить :)
    • +24
      Спасибо. Если народ заинтересуется, я постараюсь продолжить рассказывать про возможности Nginx. Так получилось, что мне приходится использовать этот веб-сервер на довольно нагруженном проекте, так что материалов набралось немало. :)
      • +9
        Народ интересуется.
      • 0
        Очень бы хотелось. Сам вот недавно поставил пока тока на тестовом. Собираюсь на продакшн кидать. Тема очень интересная.
        • +1
          Поскольку про обычную настройку и тюнинг сервера, в интернете, да и на хабре, материалов довольно много, я хочу описывать те фичи, которые используются реже. Возможно, просто потому, что ими мало кто интересовался. :)
          • 0
            Все верно. nginx был первой программой которую я скомпилировал ( в смысле удаленно и на CentOS). Однако про конфигурирование я бы почитал побольше. Материал сильно разрознен.
      • 0
        nginx рельсы вообще круто разгоняет. Прямо чувствуется.
      • +1
        да-да, такие материалы нужны!
      • +1
        Да-да, народ интересуется. Ждем-с
      • 0
        да просьба написать материалов на практическое использование и как можно более подробных
  • 0
    Добавьте в блог nginx для порядка. За статью спасибо, продолжайте в том-же духе:)
    • +4
      Я бы с удовольствием, но пока, к сожалению, могу писать только в свой личный блог. Я тут всего два дня. :)
  • 0
    Странное ограничение по дате регистрации, ну да ладно… :)
    • +1
      Мне кажется, ограничение связано с кармой, хабрасилой, или каким-то ещё параметром, в которых я пока не очень разобрался. Как только разберусь, перенесу запись. :)
  • +3
    А если вы работаете на FastCGI то
    fastcgi_cache merge;
    fastcgi_cache_valid 240h;
    fastcgi_cache_key "$hash";
  • +3
    А если пользователь изменил имя в профиле? Ему надо куки рефрешить?
    • +2
      Спасибо, что заметили. Это представляет собой некоторую проблему. Действительно, в этом случае надо, либо изменить идентификатор сессии в куке (самый простой и логичный вариант), либо какой-то ещё параметр, входящий в строку proxy_cache_key (например args).
      • 0
        Либо руками удалять файл кеша для данной страницы? Или это не самый лучший (простой) вариант?
        • +1
          Вариант не самый лучший, потому как кешем nginx заведует, причём под кеш отдельный процесс выделен — cache manager. Полагаю, что он может обидеться, если в его кеш кто-то со стороны полезет :)
        • +2
          Руками удалять файл из кеша nginx — это не наш метод. :) Тем более, что пути к закешированным файлам могут измениться, причём даже не теоретически. Например, вы решите, что 2-х уровней каталогов для вашего кеша мало, и решите добавить третий.

          Всё-таки, в данном случае проще всего просто поменять идентификатор сессии у пользователя в куке.
          • +1
            Согласен :)

            Просто раньше не задавался вопросом смены SID'а в PHP-приложениях, а теперь нашёл простое решение.
    • 0
      Да, меня этот вопрос тоже очень интересует.

      Если пользователь вышел, поменял имя, ещё что-нибудь сделал? Это как-то можно учесть при кешировании? Или придётся самому сбрасывать кеш?
      • +1
        Если пользователь вышел, то вы, вероятно, очистили ему куку с идентификатором сессии, а значит, более никаких специальных действий предпринимать не надо. Если пользователь поменял имя (или любую другую информацию, которая показывается в закешированном блоке), то вы должны ему поменять идентификатор сессии в куке (ну, и предпринять другие соответствующие действия на сервере), иначе, некоторое время (до 3х часов) ему может показываться блок со старыми данными.
  • 0
    полезно конечно, спасибо, но на мой взгляд подходит только контент-сайтам.
  • +1
    все хорошо только я не понял каким боком первое относится ко второму… мне кажется что без первого (ну т.е. всю страницу отдаем а не кусок) при применении второго нииче не поменяется, я не думаю что чуть возросший объем кешируемых данных сильно скажется на производительности, а галаздо проще и надежнее, не?
    • +2
      Да, можно кешировать страничку целиком, а не только один блок. В данном примере, действительно нет большой разницы, кешировать всю страничку, или только отдельный блок.

      Но дело в том, что динамических блоков может быть несколько, с большим количеством вариантов содержимого. И тут уже становится выгоднее кешировать именно блоки по-отдельности. :)
  • –1
    Google, например, умеет определять наличие обновляющихся блоков на страничке и добавляет ей немного кармы (читай, PR).
    И откуда же информация такая взялась?
    • +1
      Я не специалист по SEO, но мне казалось, что этот факт общеизвестен. Можно посмотреть, например, тут: webest.info/seo/google/google-pr-ratings.php (пункты 27,28,30).
      • –1
        А я успешно занимался SEO с 1997 года по 2007. Поэтому к таким статьям настроен скептически, как и к высказыванию, которое процитировал.
        Смотрим вниметельно — во-первых, в статье по этой ссылке написано:
        «38 факторов, которые, предположительно, позитивно влияют на позицию при ранжировании Google». (видите слово «предположительно»?).

        Во вторых, эти 38 чудо-факторов, которые предположительно влияют, выделены полужирным, а ни один из пунктов 27, 28, 30 не выделен.

        Прошу прощения за оффтопик, но глаз режет, когда между делом упоминается какая-нибудь ерунда.
  • +1
    А можно ли например такой блок хранить во внешнем кеше типа memcached? Например генерить набор блоков и кидать их в кеш, а nginx будет их собирать и вставлять в шаблон.
    • 0
      можно memcached на бэкэнд поставить, и из него проверять наличие блоков в кэше =)
    • 0
      В принципе, возможно, хотя лично я такую конфигурацию не пробовал. Возможно, больше про неё сможет рассказать мой друг и хабрачеловек bambr. :)
      • +2
        Неожиданная ссылка я бы сказал :)
        • 0
          Ну, просто, насколько я знаю, он как-раз делал работающую конфигурацию, когда бэкенд сам кладёт прегенерённые блоки в мемкеш, а их оттуда уже забирает nginx, которому, соответственно, на бэкенд ходить практически не надо, разве что только по большим праздникам. :)
          • 0
            Не, просто вы сами попробуйте на эту ссылку нажать))
            А про такую конфигурацию было-бы очень интересно почитать, ибо сам хочу применить нечто подобное…
            • 0
              Кхм… ой… одна маленькая опечатка, а такой результат. Жаль, что нельзя редактировать комментарии.
    • 0
      Я пытался у юзера tigrenokразузнать про такой способ работы нгинкса с мемкешем (он писал тут habrahabr.ru/blogs/startup/60915/ ) но что-то молчит)))
  • +1
    интересно, что быстрее работать будет — кэширование средствами nginx или через memcached?
    • 0
      Тестирование, главный аргумент!
    • +1
      Все просто.

      Мемкеш гоняет данные по сети (loopback тоже сеть и тоже ест CPU ПЭВМ), а значит есть накладные расходы.
      Nginx отдает статику из локальной fs. А часто отдаваемые данные из fs вообще попадают в active область памяти и отдаются так быстро, что слышно свист.

      Истина посередине: кешировать в бекенде (php, perl, python) тяжело доставаемые данные мемкешем, например, на «минуты», а уже фронтендом (nginx) кешировать всю выдачу бекенда на «секунды».

      Это решает вопрос смены имени юзера:
      — инвалидация кеша мемкеша проще реализуется на уровне бизнес-логики (php,perl,python)
      — бекенд (php, perl, python) делает трудоемкие вычисления для этого юзера всего раз в «минуты» (но при смене имени кеш дропается мгновенно)
      — фронтенд (nginx) сделает из 100 запросов в «секунды» всего один-единственный инвалидирующий запрос к бекенду

      Я, конечно, не разбираюсь в математике и компюторах, но кажется последние два пункта говорят о снижении нагрузки на два порядка.
  • 0
    классический вариант на нагруженном ресурсе именно для это задачи — главная страница чистая статика, а хеадер подтягивается аджаксом — я понимаю что из другой оперы но тоже вариант решения озвученной задачи. Модифицированный варинт но менее надежный — запихивать в куку «имя (логин)» я прямо в статике скриптом выгребать
    • 0
      В случае с аяксом увеличивается количество соединений клиент-сервер
      • 0
        я не говорю что мой метод лучше указанного, я так просто, в тему как один из вариантов и более простой в реализации
      • 0
        Зато можно не каждый раз клиенту отдавать всю страницу, а возвращать 304, не тратя трафик, и догружать динамические компоненты на js.
        Разве не именно этот вариант используется на ya.ru?

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