Кеширование на Drupal-сайте. Сравнение встроенного в Drupal кеша, статического файлового кеша (модуль Boost) и Varnish

Публикую текст своего доклада для Друпалконфа, который прошел 4 июня 2012 года в Москве. Хочу акцентировать внимание на том, что этот текст не адаптирован под блогпост и публикуется в том виде, в котором я рассказывал его на конференции.

Введение

В текущем Drupal 6 проекте, над которым я работаю последние 2 года, в пике мы отдаем до 2 млн просмотров страниц в день и забиваем полностью наш 200-мегабитный интернет-канал. Судя по отчетам нашей системы мониторинга с текущей архитектурой и железом (6 серверов: 2 фронтэнда с nginx, 2 бэкенда с Varnish + Apache + Drupal и 2 MySQL-сервера с master-slave репликацией) мы можем выдерживать втрое большую нагрузку, если решим вопрос с каналом.

За время работы этого проекта мы на нем успели применить и испытать если не все, то большинство технологий по оптимизации производительности, которые применимы к Друпалу, в том числе использовали собственные велосипеды, но в итоге пришли к использованию связки Прессфлоу + Варниш и эта связка показала себя самой производительной и простой в сопровождении (в сравнении с собственными патчами ядра и модулей).

Я хочу рассказать о том, какие для Друпала существуют средства оптимизации скорости отдачи контента и сравнить их плюсы и минусы. Я не буду вдаваться в детали и приводить примеры конфигов, это все можно найти в интернете (я у себя в блоге выложу в ближайшее время пример конфига, используемого в наших проектах). Я не буду рассказаывать об оптимизации конфигов MySQL, установке систем кеширования опкода, так как это большая тема достойная отдельного доклада, она хорошо раскрыта, в интернете можно найти много статей по оптимизации окружения, в котором работает сайт. Кроме того, эта тема имеет мало отношения к Друпалу и больше должна быть интересна системным администраторам, чем программистам (хотя программистам тоже неплохо бы в ней разбираться).

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

Я уже говорил, что наш сайт работает на шестом Друпале, но все методы, о которых я расскажу, могут применяться в седьмом Друпале и почти все — для сайтов созданных на любой CMS/фреймворке и языке программирования.

Я рассмотрю три варианта кеширования:

  1. встроенная в Друпал система кеширования и механизмы её расширяющие,
  2. файловый кеш (модуль Boost), 3.Pressflow + Varnish, 4.Альтернативы?

Встроенные механизмы

Итак, приступим. Сначала рассмотрим схему работы Друпала из коробки и встроенную в него систему кеша. Логика работы простая:

  1. пользователь открывает страницу в браузере, браузер передает запрос нашему веб-серверу,
  2. веб-сервер определяет что делать с запросом: если запрашивается статический файл (картинка, css, java-script), то веб-сервер пытается отдать этот файл сам, если запрошена динамическая страница, то запрос передается приложению, в нашем случае Друпалу.
  3. Друпал генерирует страницу, делая при этом множество запросов к базе данных, и страница по обратной цепочке уходит пользователю.

Этот подход совершенно неприменим на сколько-нибудь посещаемом сайте, обычно первой сдается, начинает тормозить и падает база данных, но, как я уже говорил, почти наверняка, разные пользователи запрашивающие на вашем сайте одну и ту же страницу будут получать один и тот же результат, по этому 1000 раз генерировать одну и ту же страницу для 1000 посетителей нет никакого смысла, правильнее один раз сгенерировать страницу при первом запросе, положить ее в кеш и следующим посетителям отдать закешированную версию страницы.

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

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

Встроенное в Друпал кеширование это уже лучше чем ничего, но все равно оно полно недостатков:

  1. для отдачи кеша нужно грузить ядро Друпала,
  2. для отдачи кеша нужно делать запрос к БД,
  3. в случае падения БД пользователи не смогут увидеть даже закешированную версию сайта,
  4. ну и, хоть это и самый простой в настройке механизм кеширования, он и самый медленный по сравнению с любыми другими альтернативами.

Для решения второй проблемы (хранении кеша в БД), можно использовать такие модули как memcache или cacherouter, их суть состоит в том, что они предоставляют альтернативные базе данных хранилища для кеша, например Memcache. Кроме того они позволяют расширить стандартный Друпальский кеш такими фичами, как перенос сессий и кеша URL’ов в мемкеш, но, несмотря на это, эти механизмы для отдачи кеша по прежнему требуют загрузку ядра Друпала, а это дорогостоящая операция.

В Друпале есть 2 режима кеширования: нормальный и агрессивный. Отличие агрессивного от нормального в том, что в нормальном режиме даже при отдаче данных из кеша Друпал вызывает хуки hook_boot и hook_exit, в агрессивном режиме Друпал этого не делает, по этой причине с агрессивным режимом некорректно работают модули, использующие эти хуки, например, модуль ядра Statistics, так как он пишет статистику именно на hook_exit.

Boost

И, таким образом, я делаю подводку к идее, что Друпал, как минимум, не должен отвечать за отдачу кеша, а как максимум еще и за его генерацию, но об этом позже. И таким решением является модуль Boost. Суть его состоит в том, что сгенерированная Друпалом страница складывается не в БД, не в мемкеш, а в файловую систему в виде html-файла. Конфиг веб-сервера дорабатывается таким образом, чтобы при запросе страницы веб-сервер сначала пытался найти закешированную страницу в файловой системе и только в случае отсутствия кеша запрашивал Друпал.

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

По сравнению с предыдущими методами этот имеет несколько больших плюсов:

  1. значительно выше скорость отдачи кеша, так как отдает его веб-сервер не трогая Друпал,
  2. как следствие, снижается нагрузка на Друпал, так как теперь он должен в основном только обслуживать зарегистрированных пользователей,
  3. при падении БД, анонимы будут продолжать получать закешированные версии страниц.

Также в плюсы Буста можно записать относительную простоту его настройки и нетребовательность к ресурсам сервера: если у разработчика хватило умений настроить Друпал, то он сможет настроить и Буст; если на сервере запустился Друпал, то на нем будет работать и Буст, не нужно устанавливать и настраивать дополнительный софт типа Мемкеша или Варниша.

К недостаткам Буста можно отнести следующие пункты:

  1. из коробки не умеет работать в случае если Друпал разнесен на несколько бэкендов, мы в свое время патчили Буст, чтобы решить эту проблему.
  2. Работает только для анонимов. Существуют ситуации, в которых подобный механизм мог бы использоваться и для зарегистрированных юзеров. Теоретически, Буст также можно пропатчить для решения этой проблемы, но на практике нам оказалось гораздо проще вообще отказаться от использования Буста в пользу перехода на Прессфлоу и Варниш.

Есть еще одна интересная идея развития подхода, который реализует Буст. Это просто идея, думаю, что применительно к Друпалу ее никто не использует, так как с Друпалом проще использовать Варниш, хотя я не раз натыкался на описание такого подхода в недрупальских проектах.

Замечательный веб-сервер nginx умеет читать данные из мемкеша, то есть также как и в случае использования Буста, веб-сервер получив запрос сначала пытается найти в файловой системе закешированную версию запрошенной страницы, nginx, получив запрос, может сначала попытаться найти закешированную версию страницы в мемкеше. То есть можно написать модуль аналогичный Бусту, который будет складывать кеш страниц в мемкеш, а отдавать его будет nginx. В отличии от Drupal-модулей memcache и cacherouter при такоим подходе кеш будет отдаваться мимо Друпала и будет работать в случае падения Друпала и БД.

Pressflow + Varnish

Еще одним минусом Буста является то, что управлять кешем таки должен Друпал, гораздо удобнее было бы, если бы кешированием занималось внешнее приложение, стоящее перед Друпалом и Друпал даже бы и не знал, что кто-то отдает контент вместо него. Плюсов у такого подхода минимум два:

  1. не нужно ставить дополнительные модули, замедляя таким образом, и без того не быструю работу Друпала,
  2. это внешнее приложение можно вынести на отдельный сервер и падение бэкенда целиком (речь о физическом выходе из строя сервера, а не сбое в софте) не навредит работе сайта.

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

В случае если в кеше Варниша запрошенной страницы нет, он передаст запрос приложению и закеширует полученную в ответ страницу.

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

Прежде чем рассказать о плюсах Варниша скажу о двух его недостатках… точнее это не недостатки, а некоторые условия:

  1. таки Варниш для своей настройки требует некоторых знаний в области администрирования *nix-систем, он сложнее в настройке, чем Буст или встроенное в Друпал кеширование, но я уверен, что профессиональный разработчик с опытом работы с *nix-системами сможет установить и настроить Варниш.
  2. Варниш требует установки себя на сервер, то есть очень вероятно, что на обычных виртуальных хостингах использовать его не получится.

Теперь о плюсах:

  1. Варниш поддерживает несколько типов хранения кеша, в том числе он может хранить его в файловой системе или в оперативной памяти. Отдача кеша из оперативной памяти сильно быстрее отдачи кеша из ФС.
  2. В случае полного падения бэкенда: сломался веб-сервер, упала база, сгорел блок питания и т.п., пользователи будут получать закешированные версии страниц. В случае с Бустом посетители получали бы ошибки 50х, Варниш же, умеет в случае падения бэкенда отдавать кеш и авторизованным юзерам, это лучше чем отдавать ошибки.
  3. Варниш позволяет сколько угодно горизонтально масштабировать систему, мне знаком проект, в котором стоит 8 фронтендов с Варнишами (по 24 Гб ОЗУ в каждом).
  4. Варниш поддерживает ESI-инструкции, это означает, что при некоторой доработке приложения, кеш Варниша может быть использован и для авторизованных юзеров. Мы сейчас ведем работу в этом направлении, под “некоторой доработкой” я понимаю замену уникальных для зарегистрированных пользователей блоков (вход в систему, форма комментариев и т.п) заглушками, которые будут заменяться контентом либо Варнишем через ESI, либо аяксом (в этом плане очень выручает модуль ctools с его очень удобным плагином ajax).
  5. Варниш быстрее отдает кеш, чем любой из описанных мною ранее механизмов, в том числе и быстрее Буста (http://cruncht.com/95/drupal-caching/). Думаю, посоперничать в производительности с Варнишем могло бы только решение со сязкой nginx + memcache, о которой я говорил выше, но такого готового решения под Друпал нет и в условиях постоянной нехватки времени Варниш является максимально удобным решением по соотношению скорость работы / скорость внедрения.

Варниш, Друпал и Прессфлоу

Из коробки Друпал 6 не умеет работать с Ванишем, и бытует мнение, что для того чтобы подружить Друпал с Варнишем нужно обязательно заменить Друпал на его форк — Прессфлоу.

Это ошибка, использование Прессфлоу это самый простой путь внедрения Варниша, но, тем не менее, подружить Варниш с родным Друпалом все же можно. Причина того, что Варниш невозможно подружить с голым Друпалом состоит в том, что Варниш должен уметь отличать авторизованного юзера от анонима. Самый простой способ сделать это — зацепиться за куку SESS*, а для этого нужно, чтобы такая кука существовала только у авторизованных юзеров, но Друпал создает сессии для всех юзеров, в том числе и анонимов, по этому эта кука существует у всех юзеров.

Одна из особенностей Прессфлоу состоит в том, что он не создает сессии для анонимов, как следствие, Варниш может по наличию/отсутствию куки SESS* решать отдавать пользователю данные из кеша или нет. Решение резать сессии для анонимов достаточно радикальное и не все сайты могут пойти на такой шаг.

Обойти проблему определения Варнишем типа пользователя (аноним / не аноним) можно было бы простым модулем, который бы ставил дополнительную куку с соответствующей информацией, но Прессфлоу имеет ряд дополнительных усовершенствований, из-за которых мы в своем проекте таки решили использовать его.

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

Выводы

Boost — простой в настройке, эффективный и нетребовательный к ПО сервера метод ускорить любой сайт на Друпае. Varnish — решение более эффективное чем Boost, но и более требовательное к профессиональным навыкам разработчика и окружению, в котоом работает сайт.

Ссылки

http://cruncht.com/slides/drupal-performance-scalability.html