Drupal 8 где хранит кэш
Плюшки, фантики и печеньки
Перепроверьте правильность всей конфигурации. Узнать об этом будет куда больнее от клиента в 2 часа ночи, чем сразу после внесения изменений. Поверьте моему опыту интуиции.
Настройка Drupal 8
На вкладке Headers:
Добавьте заголовок Cache-Tags c содержимым [invalidation:expression]. Это токенизированное выражение будет заменено на значение инвалидированного кеш тега модулем Token.
Остальное можно (и нужно) конфигурировать под ваш вкус и конкретную ситуацию. Теперь Drupal будет включать заголовок Cache-Tags во все свои ответы, куда будет вписывать список кеш-тегов, участвовавших в формировании сгенерированной страницы. Varnish будет хранить эти заголовки, как часть своего кеша и будет их использовать для инвалидации кеша.
В этом месте удостоверьтесь, что ваш Drupal при запросе напрямую (минуя Varnish) действительно содержит Cache-Tags заголовок. В моем случае он выглядит вот так для первой попавшейся страницы:
По умолчанию, Drupal отвечает Cache-Control: private, no-cache (т.е. запрещает кеширование) на все HTML ресурсы, которые он генерирует. Это значит, что Varnish не будет их кешировать. Нужно включить внешнее кеширование в настройках Drupal’а. Перейдите на страницу /admin/config/development/performance и выберите ненулевое значение для Page cache maximum age.
Я рекомендую начать с какого-нибудь безобидного 5-минутного кеша, и только когда все отлажено и успешно обкатано, переходить на полномасштабные значения длиной в часы либо дни.
Опять же, перепроверьтесь – запрашивая страницу Drupal’a анонимным пользователем, вы должны увидеть в ответ: Cache-Control: public, max-age=[ненулевое время жизни кеша] . А при запросе от имени авторизированного пользователя, в ответ должен прилететь Cache-Control: private, no-cache . Таким образом мы разрешили Varnish’у кешировать анонимные и только анонимные HTML страницы.
Расширяем Cache API на Varnish
- Purge (purge)
- Purge Drush (purge_drush)
- Purge Tokens (purge_tokens)
- Purge UI (purge_ui)
- Cron processor (purge_processor_cron) или Late runtime processor (purge_processor_lateruntime). О разнице между этими 2 модулями чуть позже.
- Core tags queuer (purge_queuer_coretags)
- Varnish Purger (varnish_purger)
- Varnish Purger Tags (varnish_purge_tags)
Хочу свой рендеринг с кешем и зависимостями
$config = \ Drupal :: config ( 'system.site' );
$current_user = \ Drupal :: currentUser ();
// Merges the cache contexts, cache tags and max-age of the config object
// and user entity that the render array depend on.
$renderer -> addCacheableDependency ( $build , $config );
$renderer -> addCacheableDependency ( $build , \ Drupal \ user \ Entity \ User :: load ( $current_user -> id ()));
?>
Еще пример
В блоге Acquia Dev есть пример, в котором рассматривается конвертация изображения в asci-символы. Вот краткая выжимка:
Как тебе такое решение?
Здесь клёвая картинка ламы будет преобразована в ASCII символы. И, будь уверен, преобразование просто прекрасно! Но сейчас разговор не об искусстве ASCII. Динамическая генерация данных на основе изображения, разве это не медленно, спросишь ты. Ясен день! В конце-то концов, как следует преобразовать пушистую ламу в набор символов не так-то и просто. Нет, без шуток, можно придумать кучу случаев затратного рендеринга (требующего сложных вычислений и кучи запросов к бд). Так что кэширование результатов здорово бы помогло. И вот как это можно устроить:
Так кому, что и почему надо знать?
При описании «революционных изменений» Drupal 8, часто упоминают «кэширование по умолчанию». Но это не просто кэширование. Два модуля, Internal Page Cache и Dynamic Page Cache затащили это дело на новый уровень. Вот как описывает историю их создания ведущий разработчик Вим Лирс (вкратце):
Как-то Дрис начал парить мне голову вопросами о ESI. Я сказал, что то, что у нас называется "поддержкой ESI" - бред, но есть реальный шанс сделать хорошо. Дриса это так проняло, что он даже выложил пост А мне пришлось изрядно напрячь башку, чтобы реализовать то, что посулил в запале.
Сначала наша команда довела до ума кэш тэги, благодаря чему Page Cache (ныне Internal Page Cache) стал работать так хорошо, что мы включили его в сборку и включили по умолчанию. А затем добили и контексты кэша. Это далось не просто, пробовали и так и сяк. В резульате на пару с Фабьеном запили через пузырёк. Потом еще 7 месяцев просыхали фиксили, но это того стоило. И теперь Dynamic Page Cache (бывший Smart Cache) тоже в сборке, и тоже по умолчанию.
Конечно, еще есть над чем работать, напрягает и время перед запуском и всякая другая лабуда, но у нас еще есть идеи на этот счет. Кстати, как вам BigPipe?
Не надо бла-бла, давай пример
Задача: разработать блок, который будет выводить значение параметра xxx переданного через адресную строку.
При рендеринге блока нет трудоемких задач, но его результат все равно будем кэшировать (иначе нафига этот пример). Чтобы убедиться, что кэш работает, кроме значения выведем еще и время, когда это значение было сохранено.
namespace Drupal \ cacheable_block \ Plugin \ Block ;
Drupal ;
use Drupal \ Core \ Block \ BlockBase ;
use Drupal \ Core \ Annotation \ Translation ;
use Drupal \ Core \ Routing ;
/**
* Provides a 'Cacheable Block' block.
*
* Block(
* id = "cacheable_block",
* admin_label = Translation("Cacheable block"),
* )
*/
class CacheableBlock extends BlockBase
$key = 'xxx' ;
$message = "" ;
$value = Drupal :: request ()-> query -> get ( $key );
$value ) $time = date ( "Y-m-d H:i:s" );
$vars = array( 'value' => $value , 'time' => $time );
$message = $this -> t ( 'value (time)' , $vars );
>
my.site?xxx=Kate - > Kate ( 2016 -02-07 11 : 11 : 11 )
my.site?xxx=Ann - > Ann ( 2016 -02-07 22 : 22 : 22 )
my.site / node / 666 ?xxx=Ann - > Ann ( 2016 -02-07 22 : 22 : 22 )
my.site?xxx=Kate - > Kate ( 2016 -02-07 11 : 11 : 11 )
Т.е. если xxx повторяется – значение берется из кэша, независимо от времени и страницы, и наоборот, стоит xxx измениться, и блок выдает другое значение (которое тоже кэширует).
Правда если зайти под другим пользователем, то будет другой кэш, хоть никакой зависимости от пользователей и не указано. И так и не понял, как задать
'cache' = > DRUPAL_CACHE_GLOBAL
Но, можно делать свой кэш с нужным значением и ключами:
$value = Drupal :: request ()-> query -> get ( $key );
$message = Drupal :: cache ()-> get ( "cacheable_block- $key - $value " )-> data ;
if( $value && ! $message ) $time = date ( "Y-m-d H:i:s" );
$vars = array( 'value' => $value , 'time' => $time );
$message = $this -> t ( 'value (time)' , $vars );
>
Drupal :: cache ()-> set ( "cacheable_block- $key - $value " , $message );
?>?php
Помни! Если кто-то вобьёт мильярд разных вариантов xxx – ничем хорошим это не закончится. Прямая зависимость кэша от столь легко меняющейся (и бесконечновариантной) зависимости – крайне не рекомендуется.
Cache hit в Varnish’е
Не нужно верить теории. После проделанной работы, лучше перепроверить на практике факт, что Varnish правильно кеширует анонимные HTML страницы! Если ваш Varnish изначально не вставляет заголовки о cache hit в свои ответы, то достаточно добавить в vcl_deliver:
Удостоверьтесь на практике, что запрашивая страницу от анонимного пользователя, Varnish ее закешировал (со второго раза он должен отвечать заголовком X-Varnish-Cache: HIT ). В этот момент спровоцируйте инвалидацию исследуемой страницы в Drupal’е – почистите кеш Drupal’a либо пересохраните ноду (если вы анализируете страницу какой-нибудь ноды) и т.п. Не забудьте запустить cron Drupal’a, если он ответственен за инвалидацию кеша в Varnish’e.
Повторите запрос на исследуемую страницу. Первый ответ должен содержать заголовок X-Varnish-Cache: MISS — таким образом вы подтвердите, что Drupal успешно уведомил Varnish об инвалидации необходимых кеш тегов, и последний успешно обработал полученный запрос на инвалидацию.
Сайты на Drupal 8 очень часто могут оказаться громоздкими. В частности это может вылиться в длинный список кеш тегов, ассоциируемых с определенной HTML страницей. Многие вебсервера и вообще Webserver + PHP стек в разных реализациях может иметь ограничение на максимальную длину заголовков ответа. Об это действительно можно споткнуться. Причем, если тестовое окружение имеет тестовый (урезанный) набор данных, то проблема всплывет только на живом окружении. Лечить проблему нужно в зависимости от того, как у вас развернут стек.
Настройка через API:
$metadata = new CacheableMetadata ( ) ;
$metadata - > setCacheContexts ( [ 'qux' ] )
- > setCacheTags ( [ 'foo:bar' ] )
- > setCacheMaxAge ( 600 ) ;
Ну и чо там?
Из Symfony в Drupal 8 привита всеобщая кэшируемость. Теперь для любого объекта, который участвует в рендеринге, можно задать свои параметры кэширования.
Всего 4 параметра (указывать все необязательно):
keys – массив значений, из которых будет сформировано название для кэша (ID). Типичный набора:
'keys' = > array ( 'entity_view' , $this - > entityTypeId , $entity - > id ( ) , $view_mode )
tags – зависимость от конкретных объектов, например от 5-ой ноды, или пользователя с Интересно, что можно задавать значения типа config:block_list (зависимость от конфигурации блоков), или config:filter.format.basic_html. А еще node_list.
В значениях тега не должно быть пробелов, т.к. пробел используются в качестве разделителя при передаче через header (странно, конечно, что выбран пробел, ну да не о том речь). Этот самый заголовок (X-Drupal-Cache-Tags) интересен для настройки супербыстрой раздачи через систему обратного проксирования (Varnish, CDN).
max-age – время хранения кэша в секундах (0 – не хранить, Cache::PERMANENT (-1) – без ограничения)
Размер очереди на инвалидацию
На странице /admin/config/development/performance/purge присутствует некоторый текущий статус. В частности, обратите внимание на счетчик Queue size. Он показывает текущую длину очереди на инвалидацию кеш тегов – это количество кеш тегов, которые уже инвалидировались в Drupal’e, но еще не были инвалидированы в Varnish’e.
Если вы используете инвалидацию через cron, убедитесь, что это число падает до нуля после запуска cron’a.
Если же у вас используется “Late runtime processor”, то вам следует ожидать ненулевые значения лишь периодически. Слишком высокая цифра, либо длительно не спадающая до 0 будет признаком каких-то проблем.
Канал коммуникации Drupal → Varnish
Что за магические значения?
Завязывать кэш на конкретные значения (5-ую ноду, 3-го пользователя) – это, конечно, не солидно. А главное, вряд ли нужные значения известны. Поэтому в реальности, конечно, всё описывается программно.
Например, нужно связать кэш со списком нод из какой-то выборки
Что там есть в Examples/cache_example
Хоть там todo больше, чем кода, но можно глянуть как вручную сохранять/получать/удалять кэш:
cacheBackend -> set ( 'название_кэша' , 'содержимое_кэша' , параметры_кэширования );
$cache = cacheBackend -> get ( 'название_кэша' ); // и потом $cache->data
cacheBackend -> delete ( 'название_кэша' );
?>?php
Где cacheBackend - это объект реализующий интерфейс CacheBackendInterface.
Там же, в качестве примера, подсчитывают количество файлов в ядре, угадаешь сколько? А вот и нет, всего лишь 5928.
Модули Cron processor и Late runtime processor
Cron processor обрабатывает очередь, когда запускается cron Drupal’а. Это значит, что cron становится стратегически важным (он и раньше должен таковым считаться). Также, из этой модели следует, что существует задержка вплоть до Х (где Х – это частота запуска cron’а), когда Varnish может отдавать закешированый и уже устаревший HTML контент. В некоторых случаях это приемлемо.
Ну и чо там?
Из Symfony в Drupal 8 привита всеобщая кэшируемость. Теперь для любого объекта, который участвует в рендеринге, можно задать свои параметры кэширования.
Всего 4 параметра (указывать все необязательно):
keys – массив значений, из которых будет сформировано название для кэша (ID). Типичный набора:
'keys' = > array ( 'entity_view' , $this - > entityTypeId , $entity - > id ( ) , $view_mode )
tags – зависимость от конкретных объектов, например от 5-ой ноды, или пользователя с Интересно, что можно задавать значения типа config:block_list (зависимость от конфигурации блоков), или config:filter.format.basic_html. А еще node_list.
В значениях тега не должно быть пробелов, т.к. пробел используются в качестве разделителя при передаче через header (странно, конечно, что выбран пробел, ну да не о том речь). Этот самый заголовок (X-Drupal-Cache-Tags) интересен для настройки супербыстрой раздачи через систему обратного проксирования (Varnish, CDN).
max-age – время хранения кэша в секундах (0 – не хранить, Cache::PERMANENT (-1) – без ограничения)
Какие есть еще API?
Теги и контексты – обычные массивы, и ничто не мешает их склеить обычным сложением
$tags = $account - > getCacheTags ( ) + $comment - > getCacheTags ( )
Но престижней использовать специально обученные метод, ведь это и инкапсуляция и избавления от повторок
Cache:: mergeTags ( $main_tags , $add_tags ) ;
Все это верно и для contexts, и для max-age.
Если в запросе нет подходящего параметра, чтобы подставить в keys, можно использовать его hash (sha256), через метод keyFromQuery.
Еще есть validateTags, invalidateTags и getBins (bins – контейнеры, в которых хранятся кэши, обычно по названию модуля, к которому принадлежат их значения)
Дебаггинг информация в Drupal watchdog
На странице /admin/config/development/performance/purge вы можете настроить необходимый уровень логирования в watchdog Drupal’a. Очень полезно, когда нужно идентифицировать проблему.
А где же та фраза?
«There are only two hard things in Computer Science: cache invalidation and naming things. -- Phil Karlton»
Еще во времена Drupal 6 и 7 с помощью Varnish’а было очень удобно кешировать статические ресурсы (рисунки, CSS, JavaScript файлы). Но были пробемы с кешированием HTML страниц – не существовало удобного механизма выборочной инвалидации кеша. Оставалось только либо сознательно отдавать устаревший кеш, либо полностью очищать кеш в Varnish при каких-либо изменениях в Drupal. Оба подхода имели свои недостатки.
Если при каком-либо изменении в Drupal’е вы не инвалидировали кеш Varnish’а, то нужно было ставить относительно маленький TTL, чтобы кеш Varnish’а достаточно быстро устаревал и Varnish обновлял свой кеш новыми данными из Drupal’а.
C другой стороны, если вы на “каждый чих” в Drupal, очищали кеш Varnish’а, то зачастую эти меры оказывались избыточными. Какой-нибудь юзер мог написать безобидный комментарий под малопосещаемой страницей, и это приводило к инвалидации всего кеша Varnish’а (в том числе и очень посещаемых страниц, которые на самом деле не поменялись из-за добавленого комментария).
Когда это будет в 7-ке?
Никогда. В Drupal 7 не появится мгновенного обновления кэша (в Drupal 8 кэш обновляется сразу по факту устаревания, не дожидаясь запроса, ога). Не появится Dynamic Cache Page. И не будет BigPipe. Как-то так.
Настройка Varnish
Теперь перейдем к подготовке Varnish’a. В ваш VCL необходимо добавить следующее.
Нужно еще добавить саму обработку Ban запросов. В ваш vcl_recv впишите в самое начало:
Так же, по умолчанию, Varnish будет ре-транслировать заголовки Cache-Tags и Cache-Control, которые он получит от Drupal’a, в свой ответ конечному клиенту (браузеру).
В случае Cache-Tags – это банально мусор, который вы будете гонять по сети и лишний источник информации о внутреннем устройстве вашей инфраструктуры. А в случае Cache-Control: public – это довольно нежелательное поведение, т.к. Drupal разрешил кеширование для Varnish’a, но кешировать страницы в браузерах ваших посетителей было бы некорректно (ведь они тогда с опозданием получат обновленный контент и вся наша умная схема про инвалидирование кешей пойдет на смарку). Посему в vcl_deliver надо добавить:
Drupal 8 Cache API
К счастью, дела обстоят куда лучше в Drupal 8, где была добавлена система гибкого кеширования через мета-информацию. Я настоятельно рекомендую любопытным почитать официальную документацию. Вкратце, эта система позволяет Drupal’у отслеживать от чего (ноды, комментарии, конфиги и т.д.) зависит сгенерированная страница. Каждая страница ассоциируется с набором кеш тегов (cache-tags). Затем, при любом изменении состояния, Drupal распознает какие кеш теги необходимо инвалидировать. Это в свою очередь позволяет Drupal’у инвалидировать лишь минимально необходимый набор закешированых страниц, когда что-то (нода, комментарий, конфиг) изменилось. Именно такой подход используется в ядре Drupal’а для инвалидирования внутреннего кеша. С помощью пары ловких движений можно добиться схожего “минимально необходимого” инвалидирования в кеше Varnish’a.
Еще раз о Cache API в Drupal 8
Описанное в статье — это лишь малая часть того, как используется cache API в Drupal 8. Я лично считаю Cache API главным новведением по сравнению с Drupal 7. Если у читателей есть интерес, то я могу написать следующую статью, где рассмотрю глубже вопросы внутреннего устройства cache API; как кеширование переплетается с рендерингом в Drupal 8.
Кэш в Drupal — это промежуточный буфер, хранящий часто запрашиваемые данные, которые могут быть возвращены пользователю с наименьшими затратами системных ресурсов и максимальной скоростью. Этот буфер может представлять из себя таблицу(ы) в базе данных, файл с данными на жёстком диске или набор ключ-значение в сетевом журналируемом хранилище данных. Подойдёт любой вариант хранения информации, который можно интегрировать с PHP для кэширования веб-приложения на Drupal.
Один из примеров использования кэша в Drupal — это кэш меню. В большинстве случаев меню не является часто изменяемой частью сайта. Не имеет смысла каждый раз перестраивать (получать все пункты, их зависимость друг от друга, выводить их в шаблон) меню для вывода пользователю при его заходе на новую страничку сайта. Тем более, что если это меню каталога из 1000 пунктов, оно может перестраиваться для вывода достаточно долгое время. Зачем же заставлять пользователя ждать? Поэтому Drupal кэширует однажды построенное меню на указанное администратором время и выводит его пользователю из кэша, избегая долгой операции по перестраиванию меню.
Drupal содержит встроенный кэш (в своих базовых модулях), который можно настроить на странице: /admin/settings/performance. Нужно не забывать его периодически чистить, когда вы добавляете новую функциональность в каких-либо своих разработках (будь то шаблоны или модули).
- С помощью кнопки «Очистить кэш» на странице /admin/settings/performance
- Установить модуль admin_menu и выбрать в самой левой вкладке Flush all caches (Очистить все кэши)
- С помощью ссылки «Empty cache» (очистить кэш) блока Devel Block (модуль Devel)
- Выполнив команду Drush: drush cache-clear theme (чистится только кэш темы)
- Программно, вызвав функцию drupal_rebuild_theme (чистится только кэш темы)
Кэш Drupal разбит по сегментам, а не хранится в одном единственном месте. По умолчанию каждый сегмент кэша хранится в виде отдельной таблицы в базе данных. Это позволяет выносить часто обновляемые сегменты кэша в другие системы хранения кэша, например в Memcached или Boost. Так же это повышает производительность работы с кэшем — в меньших объёмах данных работа с записями происходят быстрее. Более того, при определённых действиях кэш может очищаться частично (сегментно).
Drupal насчитывает множество различных сегментов кэша. Сегменты кэша похожи для версий 7.XX и 6.XX:
1. — сегмент для общего хранилища кэша. Сюда попадают данные, которые невозможно классифицировать, либо же нет смысла создавать под них новый сегмент кэша.
2. — добавляется при включении модуля Block (входит в ядро). При загрузке региона темы Drupal производится загрузка данных по всем блокам этого региона и при необходимости производится построение блока или отображение его из кэша, пропуская вызов хука hook_block_view(). Стоит учесть, что кэширование для блоков отключается, если включаются модули по работе с доступами к материалу, использующие hook_node_access(). Так же необходимо знать, что при программном создании блока через hook_block_info() можно управлять параметрами кэширования для блока (подробнее — в документации).
3. — модуль Filter создает свою таблицу для хранения кэша для обработанного фильтрами текста. Чем сложнее фильтр, тем больше процессорного времени тратится на обработку текста. Поэтому по возможности все вызовы check_markup() кэшируются. Cache ID для таблицы собирается по правилу название_формата: язык: хэш_текста.
4. — если остальные кэшируемые данные хранятся для ускорения работы сайта, то этот кэш на производительность никак не влияет. Он необходим, чтобы формы, построенные с помощью Forms API, были абсолютно безопасными с точки зрения уязвимостей. Каждый раз при построении формы она сохраняется в сегменте . Если форм и посетителей много, то имеет свойство разрастаться до внушительных размеров, если не запускать cron для очистки кэша каждый час-два.
5. — включается при включении модуля Menu и является хранилищем ссылок из всех меню, созданными через интерфейс. Cache ID строится по правилу links: имя_меню:tree-data: язык: хэш_параметров.
6. — хранит закэшированные данные страниц для анонимных пользователей. Если найден кэш для текущей страницы, то будут вызваны только 2 хука: hook_boot() и hook_exit(). Остальные же хуки (включая hook_init() и прочие) будут пропущены. Это именно тот кэш, который включается на сайте в разделе настроек производительности (admin/config/development/performance) галочкой «Кэшировать страницы для анонимных пользователей».
7. — модуль Update manager добавляет данный сегмент. Он хранит данные по всем релизам для включенных модулей.
Сегменты кэша, имеющиеся в Drupal версии 7.XX (нет в версии 6.XX):
1. — хранит соответствие между системным путём и его алиасами для более быстрого поиска алиаса по системному пути.
2. — зарезервирована модулем Image и может использоваться как хранение сведений о проведении различных манипуляций над изображениями.
3. — сегмент кэша, в котором хранятся данные, инициализируемые при загрузке Drupal.
4. — в данном сегменте хранятся данные по всем полям (fields). Cache ID формируется по правилу field: тип_сущности:id_сущности.
- cache_hacked
- cache_l10n_update
- cache_token
- cache_views
- cache_views_data
Жизненный цикл страницы
При запросе страницы у веб-сервера браузером пользователя происходит следующее (на примере Drupal версии 7.XX):
- DRUPAL_BOOTSTRAP_CONFIGURATION: Инициализирует только конфигурацию.
- DRUPAL_BOOTSTRAP_PAGE_CACHE: Инициализация слоя кэширования.
- DRUPAL_BOOTSTRAP_DATABASE: Инициализация слоя базы данных.
- DRUPAL_BOOTSTRAP_VARIABLES: Инициализация слоя переменных.
- DRUPAL_BOOTSTRAP_SESSION: Инициализация работы с сессиями.
- DRUPAL_BOOTSTRAP_PAGE_HEADER: Инициализация слоя работы с заголовками.
- DRUPAL_BOOTSTRAP_LANGUAGE: Инициализация слоя работы с языком страницы.
- DRUPAL_BOOTSTRAP_FULL: Полностью загружает Drupal. А также добавляет функции проверки и исправления введенных данных.
- Подключение файлов системных функций.
- Инициализация всех слоёв и первичных настроек.
- Подключение файлов всех включённых модулей.
- Инициализация переменных и системных функций для работы с путями и их алиасами в Drupal.
- Инициализация включённой темы оформления.
- Производится выполнении module_invoke_all(). Реализует API для загрузки и взаимодействия с модулями Drupal, регистрируя все хуки текущих включённых модулей.
- Функции drupal_deliver_html_page(), которая возвращает данные страницы в виде HTML в браузер пользователя. Внутри этой функции вызывается функция drupal_render(), которая не только выводит данные, но и сохраняет в один из сегментов кэша и достаёт их оттуда при их наличии вместо повторной генерации страницы с использованием шаблонизатора.
- Функции drupal_page_footer(), которая устанавливает кэш страницы ('cache_path' и 'cache_bootstrap'), если это необходимо, и позволяет модулям реагировать на закрытии страницы по hook_exit (). Тут же при необходимости производится запуск Cron.
- Производится инициализация всех необходимых переменных и функций.
- Производится проверка, имеется ли кэш по данному URL (). Если имеется, то он возвращается, иначе производятся дальнейшие действия.
- Производится проверка имеется ли кэш полей (), контента (), меню (), блоков (), а так же изображений () и алиасов (). Если кэш не имеется, то производится операция по получение и обработки необходимых данных с сохранением в кэш. Полученные данные передаются в функцию темизации.
- Функция темизации строит страницу и кэширует её по данному URL ().
- Данные возвращаются пользователю.
Самым распространённым вариантом работы с кэшем является сохранение данных разрабатываемого модуля в кэш используя функцию cache_set. А также извлечение их из него, используя функцию cache_get.
Очистить данные кэша с именем my_module_data можно, вызвав одну из функций:
Drupal позволяет создавать свои сегменты кэша для хранения данных. Создадим для этого модуль: mymodule. Для этого в каталоге сайта: ./sites/default/modules создадим папку: mymodule. В ней создадим файл описания модуля mymodule.info с содержимым:
Так же создадим файл mymodule.install в котором, используя hook_schema(), создадим новую таблицу для хранения данных своего сегмента кэша:
Существует малоизвестная функция cache_is_empty, с помощью которой можно узнать, хранятся ли в кэше с заданным именем какие-либо данные:
Вынесение кэша из базы данных
Кэш сегментов можно перенести, например в Memcached или Redis.
В данной статье рассмотрим вынесение кэша в Redis, который мне нравится более простой настройкой по сравнению с Memcached. А сравнительные тесты скорости обоих хранилищ рассмотрим в другой статье.
Не забывайте читать файл README.txt в папке модуля, так как в нём описаны все настройки модуля.
Где это можно применить?
Один из вариантов применения описанных знаний вынесение части данных сайта в блоки загружающиеся после основной загрузки страницы, как для ускорения загрузки сайта, так и для общего ускорения работы сайта. Достаточно лишь взять за основу модуль Ajax Blocks, интегрировать его с модулем High-performance JavaScript callback handler, а кэш данных поместить в своё хранилище в Redis, используя данную статью.
1. Отключим все неиспользуемые модули. Так как при генерации страницы перед отправкой её браузеру пользователя код определённых модулей может выполняться, даже если функционал данного модуля не используется на сайте. На выполнение кода будет тратиться процессорное время сервера, что приведёт к более долгой генерации страницы. Пример такого модуля – Statistics. Вместо статистики выдаваемой данным модулем, можно использовать статистику сервиса — Google Analytics.
2. При создании сайта используем Drupal версии 6, так как в нём лучше реализованы внутренние средства кэширования. Так же в дополнительных модулях (Views, Panel и т.д.) для Drupal версии 6 внедрены эффективные методы кэширования. К сожалению не все модули Drupal версии 5 реализованы для Drupal версии 6 (например, модуль Sphinx), не забудем об этом при планировании разработки Интернет сайта. Далее будем рассматривать только Drupal версии 6.
- компрессиию страниц;
- кеширование блоков;
- оптимизацию CSS файлов;
- оптимизацию JavaScript файлов.
При включении кэширования страниц в режим – агрессивный, при генерации страницы пропускается загрузка и выгрузка включенных модулей, поэтому часть модулей могут работать некорректно или не работать совсем.
Настроим минимальное время жизни кэша страниц для анонимных пользователей. Данный параметр определяет, через какое время после кэширования страницы, производится проверка на то обновлено ли содержимое данной страницы или нет. Если обновлено, то кэш данной страницы очищается. Т.е. если администратор сайта изменил содержимое страницы, он его увидит сразу, а анонимные пользователи, только по прошествии минимальное время жизни кэша.
Включим компрессию страниц, для сохранения сжатого кэша страниц и для передачи страницы браузеру пользователя в сжатом виде, если он поддерживает компрессию gzip. Компрессия производится с помощью библиотеки zlib установленной как расширение в PHP.
Включим оптимизацию CSS и JavaScript файлов. Это уменьшит их размер и количество обращений к серверу при загрузке страниц в браузер. Все CSS и JavaScript файлы собираются в один (свой файл для CSS и свой — для JavaScript). Что уменьшает количество обращений к серверу при загрузке страницы.
Оптимизация Drupal с помощью модулей
Authcache сохраняет сжатый кэш страниц отдельно для каждого пользователя или роли. Кэш сохраняется в базе данных или в стороннем средстве кэширования (memcahed, APC, и т.д.). Кэшированные версии страниц для аутентифицированных пользователей (кроме супер — пользователя) передаются с помощью AJAX, поэтому достигается очень быстрое отображение страницы в браузере. Если у аутентифицированного пользователя в браузере отключены JavaScript, то он получает страницы не из кэша. На некоторых серверах скорость загрузки страницы уменьшается до 1 миллисекунды.
- скачаем его;
- распакуем модуль в папку /sites/all/modules;
- скопируем файл ajax_authcache.php из папки модуля в корневую директорию сайта (там же находится файл index.php);
- откроем файл settings.php (в папке /sites/default) и добавим следующий код (без примечаний за // …) в начало файла после тега
- укажем роли, для которых необходимо кэшировать контент;
- аннулируем все пользовательские сессии (чекбокс — Invalidate all user sessions) при первом запуске;
- выставив время хранения кэша (в часах);
- нажмём кнопку сохранить и очистить кеш (Save & clear cached pages), для сохранения изменений.
Изменим, настройки элементов на страницах сайта, так что бы страница для разных пользователей, принадлежащих одной роли, выглядела одинаково (т.е. например: запретим пользователям управлять видимостью блоков на сайте, если такие блоки существуют).
- $user_name – для отображения имени аутентифицированного пользователя;
- $user_link – для отображения ссылок связанных с профилем пользователя;
- $is_page_authcache — если установлен в TRUE то все хуки данного шаблона темы оформления будут сохранены в кеш.
$conf['cache_inc'] = './sites/all/modules/cacherouter/cacherouter.inc';
$conf['cacherouter'] = array(
'default' => array(
'engine' => 'db',
'server' => array(),
'shared' => TRUE,
'prefix' => '',
'path' => 'sites/default/files/filecache',
'static' => FALSE,
'fast_cache' => TRUE,
),
);
Оптимизация конфигурации и обслуживания Drupal
От оптимизации Drupal с помощью модулей, перейдём к более сложной оптимизации – оптимизации конфигурации и обслуживания Drupal.
1. Уменьшим время хранения пользовательских сеансов. Так как Drupal хранит их в своей базе данных, то сокращение времени их хранения разгрузит базу данных, особенно если на сайт приходят тысячи пользователей в день. По умолчанию сеансы хранятся 55 часов, уменьшим время их хранения до 24 часов. Для этого на сервере в папке /sites/default в файле settings.php изменим строку:
ini_set('session.gc_maxlifetime', 86400); // 24 часа (в секундах)
Так же в этом файле можно сократить время жизни кэшированных страниц сеансов до 24 часов, изменив строку:
ini_set('session.cache_expire', 1440); // 24 часа (в минутах)
На последок в этом же файле изменим время хранения cookie в браузере пользователя, сократив его до 24 часов:
ini_set('session.cookie_lifetime', 86400); // 24 часа (в секундах)
Если установить время хранения cookie в браузере пользователя равным 0 – то cookie будет удаляться сразу после закрытия Интернет браузера пользователем.
Оптимизация сервера
1. Установим eAccelerator, он является PHP-акселератором, основное назначение которого состоит в кэшировании бинарного представления кода.
Соединимся с сервером по SSH и авторизуемся с правами root. Выполним команды для установки дополнительного пакета php5-dev:
sudo apt-get install php5-dev
sudo apt-get install make
Выполним команды для установки eAccelerator:
Отредактируем файл php.ini в папке /etc/php5/apache2, вставим в начале файла после тега [PHP] код:
; eAccelerator configuration
; Note that eAccelerator may also be installed as a PHP extension or as a zend_extension
; If you are using a thread safe build of PHP you must use
; zend_extension_ts instead of zend_extension
;extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so"
zend_extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so"
eaccelerator.shm_size = "16"
eaccelerator.cache_dir = "/var/cache/eaccelerator"
eaccelerator.enable = "1"
eaccelerator.optimizer = "1"
eaccelerator.check_mtime = "1"
eaccelerator.debug = "0"
eaccelerator.filter = ""
eaccelerator.shm_max = "0"
eaccelerator.shm_ttl = "0"
eaccelerator.shm_prune_period = "0"
eaccelerator.shm_only = "0"
eaccelerator.compress = "1"
eaccelerator.compress_level = "9"
eaccelerator.allowed_admin_path = "/var/www/eaccelerator"
При использовании Zend Optimizer и / или ionCube Loader, приведенный выше код будет выглядеть так:
; eAccelerator configuration
; Note that eAccelerator may also be installed as a PHP extension or as a zend_extension
; If you are using a thread safe build of PHP you must use
; zend_extension_ts instead of zend_extension
;extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so"
zend_extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so"
eaccelerator.shm_size = "16"
eaccelerator.cache_dir = "/var/cache/eaccelerator"
eaccelerator.enable = "1"
eaccelerator.optimizer = "1"
eaccelerator.check_mtime = "1"
eaccelerator.debug = "0"
eaccelerator.filter = ""
eaccelerator.shm_max = "0"
eaccelerator.shm_ttl = "0"
eaccelerator.shm_prune_period = "0"
eaccelerator.shm_only = "0"
eaccelerator.compress = "1"
eaccelerator.compress_level = "9"
eaccelerator.allowed_admin_path = "/var/www/eaccelerator"
; ionCube Loader configuration
zend_extension=/usr/local/lib/ioncube/ioncube_loader_lin_5.2.so
; Zend Optimizer configuration
zend_extension=/usr/local/lib/Zend/ZendOptimizer.so
zend_optimizer.optimization_level=15
Создадим кэш каталог для eAccelerator выполнив команды:
sudo mkdir -p /var/cache/eaccelerator
sudo chmod 0777 /var/cache/eaccelerator
sudo /etc/init.d/apache2 restart
2. Рекомендуем установить Web-сервер nginx и настроить его работу с Web-сервером Apache так, чтобы страницы отдавал браузеру пользователя Apache, а статический контент (CSS, JavaScript, фото и т.д.) nginx. Либо полностью заменить Web-сервер Apache, Web-сервером nginx.
- систему анализа лог файлов (например: AWstats),
- систему мониторинга производительности сервера (например: Munin);
- систему для учета сетевого трафика (например: Vnstat).
6. Включим кэш MySQL и установим его размер равным 64 мегабайтам. Для этого отредактируем файл my.cnf в папке /etc/mysql (при использовании Ubuntu 8.04). Изменим значение:
query_cache_limit = 1M
query_cache_size = 64M
После чего перезапустим MySQL командой:
Слишком маленький размер кэша – малоэффективен, а слишком большой размер кэша приводит к тому, что поиск нужной информации в кэше производится большое время. Поэтому рекомендуем поэкспериментировать с размером кэша, на каждом конкретном сервере и подобрать его оптимальный размер.
7. Проверим – загрузку центрального процессора, нехватку оперативной памяти или места на диске и возможную перегрузку линии связи сервера с Интернет. Если необходимо разместим Базу данных на отдельном сервере, для этого изменим, настройки соединения с базой данных находятся в файле settings.php (в папке /sites/default). Либо разместим сайт на кластере из серверов (например: воспользовавшись услуги сервиса Amazon C2).
Для тестирования скорости работы сайта и оптимизации его контента очень удобно пользоваться плагин YSlow для Интернет браузера Firefox, который показывает подробную статистику по загрузке страниц с вашего сайта.
Прежде чем приступить к разработке на Drupal 8 следует отключить кеш. В отличии от Drupal 7 в 8ой версии друпала кешируется не только сущности, views, поля, теперь кешируются скомплированные шаблоны twig'a и всевозможные render'ы. Для того чтобы отключить весь этот кеш, нужно сделать следующее.
1. Скопируйте файл /sites/example.settings.local.php в файл /sites/default/settings.local.php
В этом файле уже есть необходимые настройки для отключения кеша
2. В файле settings.php раскомментируйте следующие строки, это подключит созданный нами settings.local.php:
Это подключит local.settings.php.
3. Проверьте что эта строчка расскомментирована в settings.local.php:
Файл development.services.yml уже создан, нам нужно только его подключить.
4. Нужно также проверить что в settings.local.php отключена агрегация CSS и Javascript:
$config [ 'system.performance' ] [ 'css' ] [ 'preprocess' ] = FALSE ;
$config [ 'system.performance' ] [ 'js' ] [ 'preprocess' ] = FALSE ;
5. Также нужно отключить кеширование отрендеренного HTML и кеш модуля Dynamic Pages:
$settings [ 'cache' ] [ 'bins' ] [ 'render' ] = 'cache.backend.null' ;
$settings [ 'cache' ] [ 'bins' ] [ 'dynamic_page_cache' ] = 'cache.backend.null' ;
Найдите эти строки в settings.local.php и раскомментируйте их.
6. Чтобы Drupal 8 не подхватывал тесты из модулей, нужно поменять значение этой настройки на FALSE:
7. Теперь в /sites/development.services.yml нужно дописать следующий абзац:
В результате development.services.yml будет выглядеть вот так:
Очень важно соблюдать переносы строк и отступы в yml файлах, в качестве отступов используйте только 2 пробела и никаких символов табуляции.
Эта строчка отвечает за распечатывание возможных названий для переопределия каждого используемого шаблона для вывода на странице. Иногда это может ломать верстку и вызывать ошибку в некоторых модулях, хотя и очень редко. В остольном это очень удобная вещь для изучения структуры шаблонов в Drupal 8.
Entity и Config – готовы, а ты?
У всех entity или configuration уже настроены параметры кэширования. Получить их можно так:
\Drupal:: entityManager ( ) - > getDefinition ( 'node' ) - > getListCacheTags ( ) ;
Или через конкретные объекты:
$node - > getEntityType ( ) - > getListCacheTags ( )
(вместо $node, может быть $user, $view…)
Аналогично для contexts и max-age
$this - > entityType - > getListCacheContexts ( )
Яма, яма, яма, ямллл…
Кстати, простые настройки можно указывать и через YML
И еще раз
То, что эти модули включены по умолчанию, означает, что каким бы ты ни был лохом, кэширование на сайте Drupal 8 будет огонь! Только не выключай их. Хотя, если для анонимных пользователей нужны разные результаты (например, с учетом их сессии), то Internal Page Cache придется отключить (либо мутить через AJAX), но это уже другая история.
А в этой истории речь о том, что теперь больше не нужны никакие танцы с Authcache, где нужно учитывать весь код сайта. Более того, опять пошли разговоры, что владелец сайта (админ, маускликер, сайт-билдер) может забыть, что значить фраза «очистить кэш». Поскольку теперь разработчики модулей могут и должны сами настроить зависимости кэша. Контриб-модули даже обязаны предоставить тесты на годность в режиме кэширования.
Но если Dynamic Page Cache мешает запалу разработки, можно временно его отключить, для этого в папке sites/название_сайта/ создается settings.local.php с кодом:
$settings [ 'cache' ] [ 'bins' ] [ 'render' ] = 'cache.backend.null' ;
Читайте также: