ZExt Framework :: Cache

в 16:31, , рубрики: cache, framework, memcache, phalcon, php, zext, Веб-разработка

ZExt Framework :: Cache

Эта статья описывает использование модуля «Cache» PHP-фреймворка «ZExt».
Модуль отвечает за кеширование данных, получение которых требует существенных затрат времени и ресурсов.

Возможности

  • Поддержка тегов для любых хранилищ
  • Профилирование запросов
  • Гибкая архитектура компонентов позволяющая легко расширять возможности сервиса
  • Фабрика для удобного создания сервиса кеширования из имеющихся компонентов
  • Агрегация с отладочной панелью фреймворка
  • Действия с множествами данных использующие нативные методы систем кеширования


Хранение данных возможно в:

  • Memcache
  • APC
  • Файловой системе
  • В сервисе кеширования предоставляемым фреймворком Phalcon

Инициализация и конфигурирование

Фреймворк можно подключить через Composer:
"zext/zext": "dev-master"

Или получить через Github

Сервис кеширования построен по принципу Backend-Frontend компонентов, где: Backend — это компонент отвечающий за взаимодействие с системой хранения данных, Frontend — является интерфейсом доступа к сервису кеширования. Так же между этими двумя компонентами могут включатся декораторы расширяющие функционал сервиса.
В качестве вспомогательных компонентов так же присутствуют фабрики порождающие компоненты сервиса.

Наиболее простой способ создать сервис, это воспользоваться фабрикой ZExtCacheFactory:

use ZExtCacheFactory as CacheFactory;

$cache = CacheFactory::createFrontend();

По умолчанию, т.е. без параметров, будет создан backend использующий Memcache с параметрами подключения: 127.0.0.1:11211.

Передать параметры фабрике возможно через массив или через объект «Config» фреймворка. Список основных параметров:

  • type — тип Backend (memcache, apc...)
  • tags — необходимость поддержки тэгов (true/false)
  • serialize — сериализация указанного типа (json)
  • profiler — включение профилирования запросов (true/false)
  • tags_backend — отдельный backend для хранения информации о тэгах, принимает массив с описанными выше параметрами
  • lifetime (только при создании frontend или frontendFactory) — время жизни данных по умолчанию

Остальные параметры будут переданы в backend.

Рассмотрим пример создания сервиса с хранением данных в кластере из 2-х серверов Memcache, с тегами и включенным профилированием:

$cache = CacheFactory::createFrontend([
	'tags'     => true,
	'profiler' => true,
	'type'     => 'memcache',
	'servers'  => [
		['host' => '127.0.0.1', 'port' => 11211],
		['host' => '127.0.0.1', 'port' => 11212]
	]
]);

То же самое можно описать в виде INI-конфигурации:

[cache]
tags     = On
profiler = On
type     = memcache

servers.0.host = 127.0.0.1
servers.0.port = 11211

servers.1.host = 127.0.0.1
servers.1.port = 11212

use ZExtConfigFactory as ConfigFactory;
use ZExtCacheFactory  as CacheFactory;

$config = ConfigFactory::createFromFile('settings.ini');

$cache = CacheFactory::createFrontend($config->cache);

Использование

Простейший набор действий можно произвести через магические методы представляющие обращение к свойству:

// запись
$cache->key = 'value';

// чтение
echo $cache->key;

// проверка на существование
isset($cache->key);

// удаление
unset($cache->key);

Полный набор методов:

// $id           - ID или ключ по которому осуществляется доступ к данным
// $data         - собственно данные
// $lifetime     - время жизни данных в секундах (не обязательный параметр)
// $tag          - пометка данных тэгом или массивом тегов (не обязательный параметр)
// $intersection - выборка по пересечению тэгов, иначе по любому из тегов (не обязательный параметр)

// Записать
$cache->set($id, $data, $lifetime, $tag);

// Записать множество
$data = [
    $id1 => $data1,
    $id2 => $data2,
    $idN => $dataN
];

$cache->setMany($data, $lifetime, $tag);

// Прочитать
$cache->get($id);

// Прочитать множество
// Возвращает массив вида: [idN => $dataN]
$cache->getMany([$id1, $id2, $idN]);

// Получить данные по тегу или множеству тегов
// По пересечению тегов или по любому из тегов
// Возвращает массив вида: [idN => $dataN]
$cache->getByTag($tag, $intersection);

// Проверить существование данных
$cache->has($id);

// Удалить данные
$cache->remove($id);

// Удалить множество данных
$cache->removeMany([$id1, $id2, $idN]);

// Удалить по тегу или множеству тегов
// По пересечению тегов или по любому из тегов
$cache->removeByTag($tag, $intersection);

// Инкремент / декремент
$cache->inc($id, $value);
$cache->dec($id, $value);

Теги

Методы работы с тегами описывает интерфейс "TaggableInterface". Только бэкенд реализующий данный интерфейс может работать с тэгами. При попытке выполнить операцию с тегами из фронтенда, с бэкендом не реализующему таковой интерфейс, будет брошено исключение.

Бэкенды не реализующие "TaggableInterface" могут быть расширены декоратором "Taggable". Декоратор может быть снабжён отдельным бэкендом для хранения информации по тегам. Любой из бэкендов, для основных данных или для хранения тегов, может быть расширен любым декоратором, на пример ответственным за профилирование, сериализацию и т.д.

Вот так может выглядеть конфигурация с сериализацией тегов JSON'ом:

$cache = CacheFactory::createFrontend([
    'tags'      => true,
    'profiler'  => true,
    'type'      => 'file',
    'cachePath' => 'my_project/cache/data',
    'tags_backend' => [
        'type'      => 'file',
        'cachePath' => 'my_project/cache/tags',
        'serialize' => 'json'
    ]
]);

Фабрика фронтендов

Выше мы рассмотрели использования сервиса в базовом варианте. В сложных приложениях появляется опасность пересечения по ID между сервисами приложения использующих одно и то же хранилище кеш-данных. Фронтенд модуля кеширования позволяет задать пространство имён в котором он будет работать, а фабрика фронтендов позволит порождать фронтенды с различными пространствами имен для различных сервисов.

Если раньше мы могли предоставлять сервис кеширования через IoC для нашего приложения, то теперь мы будем предоставлять фабрику фронтендов в качестве такого сервиса. Инициализация будет отличаться минимально: вместо метода "createFrontend(), с параметрами или без таковых, мы будем вызывать метод "createFrontendFactory()"

use ZExtCacheFactory as CacheFactory;

$dependencyInjector->set('cacheFactory', function() use($config) {
    return CacheFactory::createFrontendFactory($config->cache);
});

Основной метод фабрики: createWrapper($namespace), так же возможно обращение к пространству имён как к свойству:

$cache = $dependencyInjector->get('frontendFactory')->createWrapper('some_api');

// Могло бы быть и короче:
$cache = $di->frontendFactory->some_api;

// Конечное ID будет: 'some_api_users_list'
$data = $cache->get('users_list');

CacheAwareTrait

Одним из компонентов модуля является трейт, содержащий ряд средств для более удобной работы с фронтендами и фабриками фронтендов. Трейт подключается в класс вашего сервиса и предоставляет логику и ряд методов облегчающих работу с кешированием.

Подключив трейт к классу, вы можете передать в него готовый фронтенд:

$myService->setCacheFrontend($cache);

либо фабрику фронтендов:

$myService->setCacheFrontendFactory($cacheFactory);

либо менеджер зависимостей фреймворка, при при реализации классом интерфейса "LocatorAwareInterface" (можно воспользоваться так же реализацией предоставляемой фреймворком, подключив трейт "LocatorAwareTrait"):

$myService->setLocator($dependencyInjector);

Для инициализации фронтенда через фабрику, класс так же должен содержать метод "getServiceName()", возвращающий строчное название сервиса.
Далее можно обращаться к методу "getCacheFrontend()" без параметров и работать с полученным фронтендом.

Так же доступны ещё несколько базовых методов для работы с кешированием: "cacheSet()", "cacheGet()", "cacheGetByTag()", "cacheRemove()", "cacheRemoveByTag()"

use ZExtCacheCacheAwareInterface;
use ZExtCacheCacheAwareTrait;

class MyService implements CacheAwareInterface {

    use CacheAwareTrait;

    protected function getServiceName() {
        return 'my_service';
    }

    public function getUsersList() {
        $data = $this->cacheGet('users_list');

        if ($data !== null) {
            return $data;
        }

        // Логика получения данных
        // ...

        $this->cacheSet('users_list', $data, 3600);

        return $data;
    }
}

При получении фронтенда или фабрики фронтендов через IoC, логика трейта будет обращаться по ID сервисов "cache" и "cacheFactory" соответственно и в описанном порядке. Так же перед этим логика трейта попытается обратиться к персональному фронтенду для сервиса по ID: "{service_name}CacheFrontend" при наличии метода "getServiceName()" в классе.

ID сервисов можно переопределить, перегрузив методы: "getCacheFrontendServiceId()" и "getCacheFrontendFactoryServiceId()".

Профилирование запросов и агрегация с отладочной панелью

За профилирование запросов отвечает декоратор "Profileable", который в свою очередь использует модуль Profiler фреймворка.
Для просмотра результатов, объект декоратора можно передать в отладочную панель через метод панели "addProfiler()". Так как положение профилирующего декоратора в цепочке декораторов может быть не известным, можно так же передать в отладочную панель фабрику фронтендов.

use ZExtDebugDebugBar;
use ZExtCacheFactory  as CacheFactory;

$debug = DebugBar::initDefaults();

$cacheFactory = CacheFactory::createFrontendFactory([
    'profiler' => true
]);

$cache = $cacheFactory->createWrapper();

// Сделаем несколько запросов
$cache->test1 = 1;
$cache->test2 = 2;
$cache->testN = 3;

$cache->getMany(['test1', 'test2']);
$cache->remove('testN');

$debug->addProfiler($cacheFactory);

echo $debug;

Теперь мы видим результаты профилирования и топологию сервиса кеширования с некоторым количеством информации о компонентах сервиса:
ZExt Framework :: Cache

Добавим поддержку тэгов и изменим запросы:

use ZExtDebugDebugBar;
use ZExtCacheFactory  as CacheFactory;

$debug = DebugBar::initDefaults();

$cacheFactory = CacheFactory::createFrontendFactory([
    'profiler' => true,
    'tags'     => true
]);

$cache = $cacheFactory->createWrapper();

$cache->set('test', 10, 0, ['tag1', 'tag2']);

$cache->setMany([
	'test1' => 10,
	'test2' => 20
], 0, 'tag');

$debug->addProfiler($cacheFactory);

echo $debug;

Видно, что появились дополнительные запросы относящиеся к информации о тегах. Так же появился компонент "Taggable" в топологии:
ZExt Framework :: Cache

Бэкенды (Backends)

Как уже было сказанно выше, бэкенды отвечают за взаимодействие с системой хранения данных. Все бэкенды реализуют интерфейс "BackendInterface", некоторые реализуют "TaggableInterface".

Memcache

Бэкенд взаимодействует с популярной системой кеширования «Memcache». Реализованны нативные методы работы с множествами данных: запись, чтение. Возможно указать пространство имён. Это может быть актуальным, при хранении данных несколькими приложениями в одном Memcache-сервере.

Параметры доступные при инициализации:

  • servers — массив memcache-серверов
  • namespace — пространство имён
  • compression — включить сжатие данных (выключено по умолчанию)
  • operationExceptions — Бросать исключение при ошибках возникающих при обращении к memcache

Массив серверов содержит настройки подключения к серверам memcache в виде массивов со следующими ключами:

  • host — адрес сервера (127.0.0.1 по умолчанию)
  • port — порт (11211 по умолчанию)
  • persistent — постоянное соединение (включено по умолчанию)

Пример конфигурации:

$config = [
    'type'     => 'memcache',
    'servers'  => [
        ['host' => '127.0.0.1', 'port' => 11211],
        ['host' => '127.0.0.1', 'port' => 11212]
    ]
];
APC

Хранит данные в памяти через PHP-расширение APC.
Не имеет настроек.

File

Хранит данные в файловой системе. Поддерживает сжатие данных, в том числе по порогу размера данных. Имеет свой сборщик мусора (удаляет не актуальные данные).

Параметры доступные при инициализации:

  • cachePath — путь к директории где будут распологатся данные (по умолчанию временная директория PHP)
  • cachePrefix — префикс имени файла с данными
  • compression — включить сжатие данных (включено по умолчанию)
  • compressionTreshold — порог после которого будет происходить сжатие данных (1Kb по умолчанию)
  • gcEnabled — включить сборщик мусора (включен по умолчанию)
Phalcon

Агрегация с сервисом кеширования фреймворка «Phalcon». Поддерживает пространства имён.

  • namespace — пространство имён
  • operationExceptions — Бросать исключение при ошибках
Dummy

Бэкенд-пустышка. Ничего нигде не хранит. Может быть полезен при разработке и тестировании приложения.

Заключение

Я надеюсь, что читатели найдут полезным для себя и своих проектов этот модуль.
В планах поддержка ещё нескольких систем кеширования, дополнительные интерфейсы взаимодействия с сервисом, сериализаторы.

Хочется услышать конструктивную критику и идеи развития.

Большая просьба воздержатся от комментариев вида «Зачем ещё один велосипед ?». Если вам не интересен проект, пожалуйста, читайте и комментируйте другие статьи, не мучайте себя нахождением в неинтересном вам посте.

Автор: mike66

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js