Invision Community предоставляет два разных, но связанных друг с другом способа кэширования данных: хранилище данных и кэширование уровней абстракции. Очень важно, чтобы оба типа кэширования считались изменчивыми (волатильными), и что данные могут быть восстановлены "на лету" по мере необходимости. Другими словами, использование слоя кэширования для ускорения приложения это хорошая идея в большинстве случаев, ваш код должен изящно обрабатывать ситуации, когда данные недоступны в кэше.
Хранилище данных
Основным методом кэширования данных в Invision Community является хранилище данных. Доступ к хранилищу данных осуществляется с помощью конструкции \IPS\Data\Store::i()->$key, где $key любой строковый ключ, который вы хотите использовать (вы должны позаботиться о предотвращении использования одинаковых ключей, выбирая достаточно уникальное имя ключа). Чтобы установить данные в хранилище данных, вам нужно всего лишь установить переменную, например:
\IPS\Data\Store::i()->my_key_name = "Данные";
Это всё: данные будут сохранены в хранилище, настроенном администратором (на диске или в базе данных).
Чтобы позднее получить значение, которое было установлено, вы просто вызываете хранилище данных таким образом:
echo \IPS\Data\Store::i()->my_key_name;
Вы также можете проверить, был ли установлен ключ, и при необходимости удалить его:
if( isset( \IPS\Data\Store::i()->my_key_name ) )
{
unset( \IPS\Data\Store::i()->my_key_name );
}
Библиотека хранилища данных позаботится об извлечении, сохранении и удалении значений автоматически при таком их использовании. Хранилище данных может быть очищено в любое время, поэтому важно сначала проверить наличие данных, перед обращением к ним. Для примера, вместо простого вывода командой echo, вы должны сделать что-то похожее:
if( !isset( \IPS\Data\Store::i()->my_key_name ) )
{
\IPS\Data\Store::i()->my_key_name = 'my value';
}
echo \IPS\Data\Store::i()->my_key_name;
Кроме того, важно, чтобы вы знали о хранилище данных при взаимодействии с централизованным контентом, например настройками. Если вы отредактируете значение настройки вручную, обновив таблицу core_sys_conf_settings, вы должны должны удалить кэш настроек в хранилище данных, иначе ваше изменение не вступит в силу сразу же. Обратите внимание, что значения хранилища данных автоматически перестраиваются по мере необходимости, поэтому вам не нужно перестраивать значение хранилища данных (просто удалите его).
\IPS\Db::i()->update( 'core_sys_conf_setting', array( 'conf_value' => 'foo' ), array( 'conf_key=?', 'bar' ) );
unset( \IPS\Data\Store::i()->settings );
Имейте в виду, что запуск инструмента поддержки, обновление и другие функции программного обеспечения полностью очистят записи хранилища данных.
Слои кэширования
В дополнение к функции хранилища данных, встроенной в Invision Community, возможно также кэширование на слоях кэширования. По умолчанию поддерживаются следующие уровни кэширования:
- Memcache
- Redis
- Wincache
- APC
- Xcache
Разработчики сторонних приложений могут добавлять поддержку других слоёв кэширования, однако пользовательский код не должен знать о конкретном используемом слое кэширования. Во-первых, стоит отметить, что любые значения, хранящиеся в хранилище данных, автоматически кэшируются на сконфигурированный альтернативный слой кэширования, если он существует. Это обрабатывается автоматически, и вы все равно должны просто вызывать \IPS\Data\Store::i()->xxxxx.
Если вашему приложению будет полезно хранить дополнительные данные в механизме кэширования, которые не нужно хранить в хранилище данных, вы также можете получить доступ непосредственно к слою кэширования. Например, Invision Community имеет возможность кэшировать страницы для гостей в течение указанного в настройках количества времени, и этот кэш не сохраняется в хранилище данных, а хранится на альтернативном кэширующем слое, если он установлен (в действительности это означает, что ваш сайт может обслуживать страницы для гостей с помощью Memcache или аналогичный движок, даже не требуя установления соединения с базой данных).
Слой кэширования автоматически работает на специальном классе None, если не настроен ни один слой кэширования, поэтому вам не нужно беспокоиться о том, настроен слой или нет. Вы просто вызываете его так же, как и хранилище данных:
if( isset( \IPS\Data\Cache::i()->my_cache_key ) )
{
echo \IPS\Data\Cache::i()->my_cache_key;
}
else
{
$variable = 'some data';
\IPS\Data\Cache::i()->my_cache_key = $variable;
echo $variable;
}
Важно помнить, что системы кэширования обычно автоматически удаляют устаревшие или наименее часто используемые данные, поэтому (например хранилище данных) все данные, хранящиеся в кэширующем слое, должны считаться изменчивыми.
Другие заметки кэширования
Помимо этих явных классов кэширования, которые вы можете использовать, есть несколько других замечаний относительно кэширования, которые также стоит упомянуть.
Встроенное кэширование
Не редко при выполнении запроса страницы один и тот же метод может вызываться несколько раз, и если этот метод выполняет ресурсоемкую работу, это может замедлить ваше приложение. Хорошая практика, если это возможно, кэшировать данные, которые были извлечены или вычислены один раз и возвращать эти же данные. В большинстве случаев это так же просто, как и следующее (предположим, что это псевдокод внутри класса):
/**
* @brief Свойство для временного хранения вычисленных данных
*/
protected $temporaryCache = NULL;
/**
* Этот метод что-то высчитывает трудоёмко
*
* @return string
*/
public function resourceIntensiveMethod()
{
if( $this->temporaryCache !== NULL )
{
return $this->temporaryCache;
}
// Do some work
$variable = '....';
$this->temporaryCache = $variable;
return $this->temporaryCache;
}
Предпосылка этого псевдокода заключается в том, что когда вызывается наш ресурсоёмкий метод, сначала проверяем, не выполнили ли мы уже эту работу, и если да, то мы её вернем из кэша. Если нет, мы выполняем работу, которую нам нужно выполнить, но затем сохраняем результат в переменной, чтобы в следующий раз мы могли её вернуть быстрее. Значения не будут сохраняться при загрузке другого процесса или страницы, но если этот метод занял 2 секунды для выполнения и был вызван 5 раз, для загрузки страницы потребуется не менее 10 секунд. Кэшируя данные после первого вызова, мы можем снизить время загрузки страницы до 2 секунд при первом выполнении этого метода. Однако, имейте ввиду, что использовать кэширование необходимо тогда, когда это целесообразно. Например, PHP имеет memory_limit и хранение слишком большого количества данных в свойствах класса может привести к тому, что ваше приложение будет быстро заполнять лимит памяти, что приведёт к ошибке PHP. При работе над вашим приложением вам нужно будет решить, какие данные стоит кэшировать, а какие нет. Такие инструменты, как XDebug, могут помочь вам определить проблемные места в вашем коде, чтобы сосредоточиться над их корректировкой.
Кэширование HTML/Ресурсов
Еще один способ воспользоваться кэшированием - это дать указание браузерам кэшировать ответ, который им отправляется. Стоит упомянуть, что на кеширование на основе браузера нельзя полагаться основательно. Некоторые User agent могут игнорировать отправленные им заголовки кэширования, могут быть настроены прокси-серверы, которые могут игнорировать заголовки кэширования (или могут кэшировать ресурсы неожиданно) и т.д. Кроме того, вы вообще можете не хотеть, чтобы браузеры кэшировали наиболее динамические сгенерированные страницы, т.к. они будут различаться при каждой загрузке (при первой загрузке вы можете быть гостем, затем вы можете авторизоваться и просмотреть страницу в качестве авторизованного пользователя, к пример). Поэтому вы должны быть осторожны и не полагаться на кеширование на основе браузера. Тем не менее, если вы отправляете статический ресурс в браузер, вы можете сказать браузеру кэшировать файл, чтобы уменьшить общую нагрузку на сервер для последующих запросов.
Чтобы отправить заголовки кэширования с ответом, вы должны отправить соответствующие заголовки ответов HTTP в 4 параметре, переданном в \IPS\Output::i()->sendOutput(). Метод доступен для вас, чтобы помочь установить заголовки.
\IPS\Output::i()->sendOutput( "Вывод для кэша, например файл", 200, 'text/html', \IPS\Output::getCacheHeaders( time(), 360 ) );
Первый параметр getCacheHeaders() это время последнего изменения файла, второй - как долго кэшировать ответ. Это используется, например, при загрузке вложения, если пользователь попытается повторно загрузить вложение в течение короткого периода времени, его браузер может извлечь уже загруженный файл из кеша браузера, не запрашивая его снова с сервера.