Статьи

Изучение Cache API в Drupal 8

Drupal 8 имеет множество улучшений по сравнению со своим предшественником, которого мы полюбили и ненавидим. Помимо известных систем, таких как Views в ядре, управления конфигурацией или полезной службы перевода, есть и менее известные изменения, но они одинаково важны для понимания и использования. Одним из таких улучшений стал API кеша, который решает многие проблемы с производительностью, которые есть у нас в Drupal 7.

drupal8wide

В этой статье я хочу немного осветить новый API кеша. С этой целью мы рассмотрим, как мы можем использовать его в наших пользовательских модулях, поскольку нам предлагается делать гораздо больше в Drupal 8.

Кроме того, я подготовил небольшую демонстрацию в виде модуля, который вы можете установить для тестирования влияния API-интерфейса кеша. Это простая страница, которая в своей логике рендеринга выполняет внешний вызов API (для фиктивной конечной точки JSON ) и кэширует свои результаты. Затем на странице отображается фактическое время, необходимое для этого, в отличие от времени внешнего вызова и времени версии в кэше.

Новый API кеша

Концепция кеширования.

Бункеры

Новый API кеша (с хранилищем DatabaseBackend по умолчанию) хранится в нескольких ячейках, которые отображаются в таблицы, начинающиеся с префикса cache_ . При взаимодействии с кешем мы всегда начинаем с запроса кеша:

 $cache = \Drupal::cache(); 

Где $cache будет экземпляром объекта DatabaseBackend , представляющим корзину по умолчанию ( cache_default ). Для запроса конкретного бина мы передаем имя в конструкторе:

 $render_cache = \Drupal::cache('render'); 

Где $render_cache будет представлять корзину кэша рендеринга (что является новым в Drupal 8 и должно улучшить производительность рендеринга по всем направлениям).

Как видите, мы запрашиваем службу кэширования статически, используя класс \Drupal . Если мы работаем внутри классов, лучше всего внедрить сервис из контейнера . Вы можете сделать это, указав в качестве аргумента для вашей службы соответствующую службу кеша (например, cache.default ). Здесь вы можете получить список всех основных служб, в том числе связанных с кешем.

Но для краткости мы будем использовать его здесь статически.

Извлечение кэшированных объектов

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

 $cache = \Drupal::cache()->get('my_value'); 

Это так просто. $cache будет объектом stdClass содержащим некоторые метаданные об элементе кэша плюс фактические данные, доступные в свойстве $cache->data . Параметр my_value является идентификатором кэша.

Важно помнить, что использование метода get() без второго параметра не вернет элемент кэша, если он был признан недействительным (либо программно, либо по истечении срока действия). Передача логического true в качестве второго параметра заставит его вернуть данные.

Хранение кеша

Хотя хранить новые элементы в кэше так же просто, как и извлекать их, у нас есть больше возможностей для этого. Для хранения элемента мы используем метод set() (вместо get() как раньше), метод, который принимает 2 обязательных параметра и 2 необязательных:

  • идентификатор кэша (строка, по которой мы позже можем ссылаться на элемент)
  • данные (значение PHP, такое как строка, массив или объект, который сериализуется автоматически и сохраняется в таблице — не должен превышать 1 МБ)
  • время истечения (временная метка в будущем, когда этот элемент кеша автоматически станет недействительным или -1 что в основном означает, что этот элемент никогда не истекает. Рекомендуется использовать константу Drupal\Core\Cache\CacheBackendInterface::CACHE_PERMANENT для представления этого значения )
  • тэги (массив кеш-тэгов, по которым этот элемент может быть позже идентифицирован)

В качестве примера:

 Drupal::cache()->set('my_value', $my_object, CacheBackendInterface::CACHE_PERMANENT, array('my_first_tag', 'my_second_tag')); 

Это установит постоянный элемент кэша, помеченный двумя тегами, и сохранит сериализованную версию $my_object в качестве данных.

Аннулирование и удаление кэша

Аннулирование кэша означает, что соответствующие элементы больше не являются свежими и ненадежными с точки зрения того, какие данные они хранят. Они будут удалены при следующей сборке мусора, которую также можно вызвать с помощью garbageCollection() объекта CacheBackend.

Как уже упоминалось выше, при хранении элемента кэша мы можем указать время истечения. По истечении этого времени элемент кэша становится недействительным, но все еще существует в корзине и может быть получен. Однако мы также можем аннулировать элементы вручную, используя методы invalidate() , invalidateMultiple() или invalidateAll() объекта CacheBackend.

Удаление элементов в целом можно выполнить с помощью методов delete() , deleteMultiple() или deleteAll() . Эти действия также происходят только в том случае, если CacheBackend упаковывает и полностью удаляет соответствующие записи таблицы.

Теги кеша

Еще одна интересная новая функция Cache API в Drupal 8 — теги кеша (четвертый параметр в методе setter). Роль тегов состоит в том, чтобы идентифицировать элементы кэша в нескольких ячейках для правильной аннулирования. Целью является возможность точного таргетинга нескольких элементов кэша, которые содержат данные об одном и том же объекте, странице и т. Д. Например, узлы могут появляться как на странице, так и в представлении (хранятся в разных элементах кэша в разных ячейках, но оба помечены как тот же node:nid отформатированный тег node:nid ). Это позволяет сделать недействительными оба элемента кэша, когда изменения происходят с этим узлом, без необходимости знать идентификаторы кэша.

Чтобы вручную сделать недействительными кэши с помощью тегов, мы можем использовать метод invalidateTags() статически в классе \Drupal\Core\Cache\Cache :

 \Drupal\Core\Cache\Cache::invalidateTags(array('node:5', 'my_tag')); 

Это вызовет службу аннулирования кэша и my_tag недействительными все элементы кэша, помеченные node:5 и my_tag .

Кроме того, для сущностей Drupal нам не нужно создавать собственные теги, но мы можем извлечь их из системы сущностей:

  • \Drupal\Core\Entity\EntityInterface::getCacheTags()
  • \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags()

Это сохраняет тэги для сущностей Drupal единообразными по всем направлениям.

Демонстрация API кеша

Как я уже упоминал ранее, я создал небольшой модуль, который позволяет нам увидеть преимущества кэширования данных. Вы можете найти модуль в этом git-репозитории, но вот суть его:

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

Файл маршрута, который добавляет новый маршрут к пути /cache-demo :

 cache_demo_page: path: 'cache-demo' defaults: _controller: '\Drupal\cache_demo\Controller\CacheDemoController::index' _title: 'Cache demo' requirements: _permission: 'access content' по cache_demo_page: path: 'cache-demo' defaults: _controller: '\Drupal\cache_demo\Controller\CacheDemoController::index' _title: 'Cache demo' requirements: _permission: 'access content' 

И класс контроллера, который возвращает страницу внутри src/Controller/CacheDemoController.php :

 <?php /** * @file * Contains \Drupal\cache_demo\Controller\CacheDemoController. */ namespace Drupal\cache_demo\Controller; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Url; use \GuzzleHttp\Client; /** * Cache demo main page. */ class CacheDemoController extends ControllerBase { public function index(Request $request) { $output = array(); $clear = $request->query->get('clear'); if ($clear) { $this->clearPosts(); } if (!$clear) { $start_time = microtime(TRUE); $data = $this->loadPosts(); $end_time = microtime(TRUE); $duration = $end_time - $start_time; $reload = $data['means'] == 'API' ? 'Reload the page to retrieve the posts from cache and see the difference.' : ''; $output['duration'] = array( '#type' => 'markup', '#prefix' => '<div>', '#suffix' => '</div>', '#markup' => t('The duration for loading the posts has been @duration ms using the @means. @reload', array( '@duration' => number_format($duration * 1000, 2), '@means' => $data['means'], '@reload' => $reload )), ); } if ($cache = \Drupal::cache()->get('cache_demo_posts') && $data['means'] == 'cache') { $url = new Url('cache_demo_page', array(), array('query' => array('clear' => true))); $output['clear'] = array( '#type' => 'markup', '#markup' => $this->l('Clear the cache and try again', $url), ); } if (!$cache = \Drupal::cache()->get('cache_demo_posts')) { $url = new Url('cache_demo_page'); $output['populate'] = array( '#type' => 'markup', '#markup' => $this->l('Try loading again to query the API and re-populate the cache', $url), ); } return $output; } /** * Loads a bunch of dummy posts from cache or API * @return array */ private function loadPosts() { if ($cache = \Drupal::cache()->get('cache_demo_posts')) { return array( 'data' => $cache->data, 'means' => 'cache', ); } else { $guzzle = new Client(); $response = $guzzle->get('http://jsonplaceholder.typicode.com/posts'); $posts = $response->json(); \Drupal::cache()->set('cache_demo_posts', $posts, CacheBackendInterface::CACHE_PERMANENT); return array( 'data' => $posts, 'means' => 'API', ); } } /** * Clears the posts from the cache. */ function clearPosts() { if ($cache = \Drupal::cache()->get('cache_demo_posts')) { \Drupal::cache()->delete('cache_demo_posts'); drupal_set_message('Posts have been removed from cache.', 'status'); } else { drupal_set_message('No posts in cache.', 'error'); } } } 

Внутри метода index() мы делаем быструю проверку, чтобы увидеть, присутствует ли параметр запроса clear в URL, и вызываем метод clearPosts() отвечающий за удаление элемента кэша. Если его нет, мы рассчитываем, сколько времени loadPosts() методу loadPosts() чтобы вернуть его значение (это могут быть либо сообщения из кэша, либо из API). Мы используем Guzzle для вызова API, а когда мы делаем, мы также сохраняем результаты напрямую. Затем мы просто выводим продолжительность вызова в миллисекундах и печатаем 2 разные ссылки в зависимости от того, хранится ли кэш или нет (чтобы мы могли очистить элемент кэша и снова запустить вызов API).

При первом переходе к cache-demo выполняется вызов API, и 100 сообщений сохраняются в кеше. Затем вы можете перезагрузить страницу и посмотреть, сколько времени потребуется для того, чтобы эти сообщения были извлечены из кэша. После этого у вас будет ссылка для очистки кэша (путем обновления страницы с clear строкой запроса), за которой следует другая ссылка, которая обновляет страницу без clear строки запроса и, в свою очередь, снова вызывает API. И так далее, чтобы проверить контраст по продолжительности.

Вывод

В этой статье мы рассмотрели, как просто использовать Cache API в Drupal 8. Есть несколько очень простых методов класса, которые мы можем использовать для управления элементами кэша, и стало слишком просто, чтобы мы не начали использовать его в наши пользовательские модули. Я призываю вас проверить его, поиграться с API и убедиться, насколько легко им пользоваться.