Мои мысли и проекты
Создаем сайт на Lumen (Laravel) - Кеширование
Кто такой уничтоженный боец шайба.

Создаем сайт на Lumen (Laravel) - Кеширование

Работа с КЕШ-ем при мультисайтинге

Для корректной работы мультасайтинга необходимо доработать ещё один компонент - КЕШ. В Lumen есть кеширование, но оно одно на все сайты. Мне же нужно сделать свой КЕШ, который будет работать в пределах текущего SID. Для этого создам модуль Cache с singleton-ом с именем SCache. Реализация весьма простая, просто буду перенаправлять все вызовы на обычный Cache, но перед этим добавлять к ключу текущий SID.

<?php
namespace App\Modules\Cache;
use Cache;
use App\SID;

class Singleton implements \App\ISingleton
{
	// Получить имя псевдонима 
	static public function getAlias() { return 'SCache'; }
	// Используемое хранилище
	protected $store=null;
	function __construct($name=null) {
		// Используемое хранилище. По умолчанию текущее
		$this->store = Cache::store($name);
	}
	// Преобразовать ключ
	protected function _key($key) {
		// Добавить к ключу текущий SID
		return SID::get().'://'.$key;
	}
	// Вернуть объект для работы с другим типом КЕШ-а
	public function store($name) {
		return new Singleton($name);
		
	}
	// Перегзуить методы
	public function __call( string $name, array $arguments) {
		// Первый параметр - это ключ. Преобразовать его
		$arguments[0] = $this->_key($arguments[0]);
		// Вызвать метод
		return  call_user_func_array([$this->store,$name],$arguments);
	}
}

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

	// Получить значение и расчитать его с помощью функции обратного вызова если такого значения нет или это режим отладки
	public function rememberGet($key,$callback,$isTransform=true) {
		if($isTransform) {
			$key = $this->_key($key);
		}
		$isDebug = config('app.debug');
		if( $isDebug || !$this->store->has($key) ) {
			$ret = $callback();
			if(!$isDebug) {
				$this->store->forever($key,self::toSerialize($ret));
			}
		} else {
			$ret = unserialize($this->store->get($key));
		}
		return $ret;
	}

Cохранение данных в КЕШ до события

Создам метод для сохранения данных в КЕШ до наступления события. Т.е. данные будут удалены из КЕШ-а при наступлении определенного события. Этим методом можно пользоваться при кешировании запросов в БД. К примеру запрос из БД статьи можно закешировать до события изменения этой статьи. В этом случае будет минимизировано количество запросов в БД. Прототип метода:

rememberEvent($arg1,$arg2,...,$argN,$callback)

Из аргументов $arg1...$argN буду генерировать ключ для кеширования. А метод $callback будет вызываться для генерации данных если их нет в КЕШ-е. Схема работы функции

На схеме видно что сначала данные проверяются в КЕШ-е (2), а затем и в таблице БД (3). Связано это с тем, что некоторые виды КЕШ-а при переполнении могут обнуляться и в этом случае данные будут присутствовать в таблице БД, но отсутствовать в КЕШ-е. В этом случае нужно будет возвращать их из БД без вызова функции генерации данных и сохранять в КЕШ-е.

Также нужно предусмотреть возможность дочерних вызовов. Т.е. внутри функции генерации могут быть вызовы функции rememberEvent. Такой вызов будем называть дочерним. В случае разрушения(очистки) дочернего КЕШа родительский тоже должен быть разрушен(очищен). Поэтому при сохранении нужно учитывать иерархию вида "родительский-дочерние элемент".

Для сохранения данных в таблицу БД будем использовать три таблицы.

  • shasoft_cache_entrys - элементы КЕШ-а
  • shasoft_cache_events - события, привязанные к каждому элементу КЕШ-а
  • shasoft_cache_hiers - иерархия элементов КЕШ-а

Функция генерации данных имеет следующий вид

/**
  $arg1,...,$argN -аргументы функции rememberEvent($arg1,...,$argN,$callback) 
*/
function callback($arg1,...,$argN) {
...
}

Для получения объекта контроля используется метод SCache::control

SCache::control()->event(...)->event(...)->on(...);

Объект контроля элемента КЕШ-а содержит метод который нужно использовать в функции генерации данных для привязки событий

/**
  Привязать событие к элементу КЕШ-а. При наступлении этого события данный элемент КЕШ-а (и все его родители) будет удален
  $arg1,...,$argN -аргументы события
*/
public function event($arg1,...,$argN)

Также есть метод в котором можно привязать функции для событий set(установка данных) и remove(удаление данных)

/**
  Привязать своих действий к элементу КЕШ-а. При наступлении этого события данный элемент КЕШ-а (и все его родители) будет удален
  $arg1,...,$argN -аргументы события
*/
public function on($name/* set или remove */,$fn)

В SCache также создадим методы для генерации событий.

    /** 
     * Начать транзакцию отправки событий
     */ 
    public function sendStart()
    /** 
     * Закончить транзакцию отправки событий
     */
    public function sendEnd()
    /** 
     * Отправить сообщение
     */
    public function sendEvent(...$arguments)

Метод sendStart начинает транзакцию отправки событий, метод sendEnd заканчивает эту транзакцию отправки событий. Метод sendEven посылает событие. Точнее он отправляет событие в буфер. Событие будет отправлено системе после вызова метода sendEnd. При этом если два раза вызвали sendStart, то отправка событий будет произведена только после второго вызова sendEnd. Такой подход позволит накапливать события и отправлять их в систему одним пакетом. После вызова sendEnd весь КЕШ, который построен на отправленных событиях, будет уничтожен.

Типичный пример кеширования до наступления события (как раз из этого метода сделана картинка лога):

    $data = SCache::rememberEvent(123, function ($id) {
        SCache::control()->event('test');
        $data = SCache::rememberEvent(123, 345, function ($id) {
            SCache::control()->event('test2');
            return "Валера";
        });
        //
        return 'Диана & ' . $data;
    });

И код для информирования системы о произошедшем событии. При его вызове будет сброшен КЕШ, который генерируется кодом выше.

	// Начать транзакцию отправки сообщений
	SCache::sendStart();
	SCache::sendEvent('test1');
	SCache::sendEvent('test2');
	// Закончить транзакцию и отправить события системе
	SCache::sendEnd();
	// Данное событие не будет отправлено, так как оно отправляется вне транзакции
	SCache::sendEvent('test3');

Так как вызывается событие test2 дочернего элемента КЕШ-а, то будет удален и он и родительский элемент.

Я планирую встроить в модель систему отправки событий изменения атрибутов модели вида

sendEvent('change',<имя класса модели>,<имя поля>)

что позволит вешать события на изменения этих полей. Типичный пример

SCache::sendStart();
$user = Model\User::find(1);
$user->name = 'Валера';
$user->save();
SCache::sendEnd();

В этом случае будет отправлено событие

sendEvent('change',Model\User::class,'name')

Если не делать вызов SCache::sendStart()/SCache::sendEnd() то событие отправлено не будет.

0

Комментарии

Чтобы оставлять комментарии войдите на сайт. Вы можете сделать это через социальную сеть