4064 читателя, 405 постов
Администрация
Модераторы
Блог для обмена опытом
- <?php
- if ($this->_Cache->IsActual()) {
- return $this->_Cache->Read();
- }
- else {
- $Content = $this->Parse($File);
- $this->_Cache->Write($Content);
- return $Content;
- }
- ?>
* This source code was highlighted with Source Code Highlighter.
- <?php
- public function IsActual()
- {
- clearstatcache();
- if (!file_exists($this->_Id)) {
- return FALSE;
- }
- if (!($CreateTime = filemtime($this->_Id))) {
- return FALSE;
- }
- if (($CreateTime + $this->_Expired) < time()) {
- @unlink($this->_Id);
- return FALSE;
- }
- return TRUE;
- }
- ?>
* This source code was highlighted with Source Code Highlighter.
- <?php
- interface Cachebackend {
- /**
- * @param string $Key
- */
- public function GetCache($Key);
- /**
- * @param string $Key
- * @param int $ExpiredPeriod
- */
- public function IsActual($Key, $ExpiredPeriod);
- /**
- * @param string $Key
- * @param int $ExpiredPeriod
- * @param int $SoonPeriod
- * @param string $Postfix
- */
- public function IsNeedNewCache($Key, $ExpiredPeriod, $SoonPeriod, $Postfix);
- /**
- * @param string $Key
- * @param string $Data
- */
- public function PutCache($Key, $Data);
- /**
- * @param stirng $PreparedKey
- * @param string $RenameKey
- * @return bool
- */
- public function Rename($PreparedKey, $RenameKey);
- }
- ?>
* This source code was highlighted with Source Code Highlighter.
- <?php
- class Cacher {
- /**
- * @var Cachebackend
- */
- private static $_Backend = NULL;
- /**
- * @var mixed
- */
- private static $_CallbackSignature = array();
- /**
- * @var array
- */
- private static $_CallbackArguments = array();
- /**
- * @static
- * @param string $Tag
- * @param string $Key
- * @param int $ExpiredPeriod
- * @param int $SoonPeriod
- * @param sting $Postfix '.next'
- * @return mixed
- */
- public static function GetData($Tag, $Key, $ExpiredPeriod, $SoonPeriod, $Postfix = '.next')
- {
- $Key = CACHE_PATH . strtolower($Tag) . '_' . strtolower($Key) . CACHE_EXT;
- if (NULL === self::$_Backend) {
- self::$_Backend = new Cachefilebackend;
- }
- if (self::$_Backend->IsActual($Key, $ExpiredPeriod)) {
- $Data = self::$_Backend->GetCache($Key);
- if(self::$_Backend->IsNeedNewCache($Key, $ExpiredPeriod, $SoonPeriod, $Postfix)) {
- $CallbackData = self::_GetCallbackResult();
- self::$_Backend->PutCache($Key . $Postfix, $CallbackData);
- }
- return $Data;
- }
- else {
- if(self::$_Backend->IsActual($Key . $Postfix, $ExpiredPeriod)) {
- $Data = self::$_Backend->GetCache($Key . $Postfix);
- self::$_Backend->Rename($Key . $Postfix, $Key);
- return $Data;
- }
- else {
- $CallbackData = self::_GetCallbackResult();
- self::$_Backend->PutCache($Key, $CallbackData);
- return $CallbackData;
- }
- }
- }
- /**
- * @static
- * @param string $CallbackFunction
- * @param array $CallbackArguments
- * @param mixed $CallbackObject NULL
- */
- public static function SetCallback($CallbackFunction, $CallbackArguments, $CallbackObject = NULL)
- {
- if ($CallbackObject) {
- self::$_CallbackSignature = array($CallbackObject, $CallbackFunction);
- }
- else {
- self::$_CallbackSignature = $CallbackFunction;
- }
- self::$_CallbackArguments = $CallbackArguments;
- }
- /**
- * @static
- * @return mixed
- */
- private static function _GetCallbackResult()
- {
- self::_CheckCallback();
- return call_user_func_array(self::$_CallbackSignature, self::$_CallbackArguments);
- }
- /**
- * @static
- */
- private static function _CheckCallback()
- {
- if(!is_callable(self::$_CallbackSignature, FALSE, $CallableName)) {
- throw new Exception($CallableName . ' is not correct callback');
- }
- if(!is_array(self::$_CallbackArguments)) {
- throw new Exception('Callback arguments must be an array');
- }
- }
- }
- ?>
* This source code was highlighted with Source Code Highlighter.
- <?php
- class Cachefilebackend implements Cachebackend {
- /**
- * @see Cachebackend
- */
- public function GetCache($Key)
- {
- clearstatcache();
- if (file_exists($Key)) {
- $Content = '';
- $Content = @file_get_contents($Key);
- return unserialize((string)$Content);
- }
- else {
- throw new Exception('Cached keyfile "' . $Key. '" does not exists');
- }
- }
- /**
- * @see Cachebackend
- */
- public function IsActual($Key, $ExpiredPeriod)
- {
- clearstatcache();
- if (file_exists($Key)) {
- if (time() - filemtime($Key) > $ExpiredPeriod) {
- @unlink($Key);
- return FALSE;
- }
- else {
- return TRUE;
- }
- }
- return FALSE;
- }
- /**
- * @see Cachebackend
- */
- public function IsNeedNewCache($Key, $ExpiredPeriod, $SoonPeriod, $Postfix)
- {
- clearstatcache();
- if (file_exists($Key)) {
- if ((time() - filemtime($Key) > $ExpiredPeriod - $SoonPeriod) && (
- (!file_exists($Key . $Postfix)))) {
- return TRUE;
- }
- else {
- return FALSE;
- }
- }
- else {
- return FALSE;
- }
- }
- /**
- * @see Cachebackend
- */
- public function PutCache($Key, $Data)
- {
- if($Key) {
- @file_put_contents($Key, serialize($Data));
- }
- else {
- throw new Exception('Cache key is empty');
- }
- }
- /**
- * @see Cachebackend
- */
- public function Rename($PreparedKey, $RenameKey)
- {
- return @rename($PreparedKey, $RenameKey);
- }
- }
- ?>
* This source code was highlighted with Source Code Highlighter.
- <?php
- //Инициализируем пользователя
- $User = new User();
- //Инициализируем данные пользователя
- $UserData= new UserData($User->Id);
- //Настраиваем кеширование данных пользователя
- Cacher::SetCallback('GetData', array($User->Id), $UserData);
- //Получаем данные пользователя
- $Response = Cacher::GetData('userdata', md5($User->Id), 24 * 60 * 60, 60 * 60);
- //Настраиваем кеширование текста пользователя
- Cacher::SetCallback('GetUserText', array($User->Id));
- //Получаем текст пользователя
- $Response .= Cacher::GetData('usertext', md5($User->Id), 24 * 60 * 60, 120 * 60);
- //Отдаем результат
- echo $Response;
- ?>
* This source code was highlighted with Source Code Highlighter.
комментарии (40)
лично я событием для синхронизации кэша выбираю запись измененного контента
1) обновлять кэш ТОЛЬКО в том случае, если данные действительно обновились. А это значит нужно где-то держать флаг изменения данных
2) обновлять кэш отдельным процессом, а не в момент, когда обращается пользователь. Это приводит к тому, что пользователь видит ВСЕГДА КЭШ. А вот обновление происходит на кроне в *nix (или расписании под винду). Какие выгоды: 1. нет никаких конфликтов, потому что файлы обновляет один единственный процесс вне зависимости от количества пользователей и частоты их обращения, 2. нагрузка распределенная, то есть потенциально сервер нагружен равномерно, а не большими пиками в момент массового обновления кэша.
3) кэш формировать отдельным именем, а потом простыми операциями заменять старый файл на новый готовый (когда он уже не формируется)
4) иногда думал об использовании версионности кэша. Это значит, что создаются файлики типа cache.block12345.67890, где 67890 — это версия кэша. Допустим где-то в базе данных лежит привязка на то, что актуальный кэш лежит в версии 67889. Потом фоновый процесс создает файлик 67890 и заменяет привязку к версии на более актуальную. Это приводит к тому, что нет сложных файловых операций. По сути происходит лишь создание нового файла. В нагруженных системах очень хорошо работает. 1000-чи пользователей сначала обращаются к 67889, а потом единомоментно начинают читать 67890… переход от одной версии к другой в этом случае происходит со скоростью обновления записи в БД.
Только вот одно замечание — часто кеш делается так чтобы к базе вобще не обращаться. Поэтому текущую версию кеша лучше все-таки хранить в виде обычной константы в файлике настроек, или в ENV или еще где-то, откуда её можно считать без дополнительных затрат.
Насчет первого пункта могу сказать, что обновляться могут большие объемы данных, поэтому их можно обновить по частям в разные моменты времени.
unlink cache.block12345 && link -s cache.block12345 cache.block12345.newversion
в винь — ярлыки?… в целом да, в случае, когда нет желания хранить актуальную версию в базе, можно хранить в линке. Согласен
2. Нет. Проще. Класть кешированные страницы как html-файлы в папку public. Так в рельсах при полном кешировании страницы делается, например.
Думаю веб-сервер это не сможет.
1.1 Если промежуточные данные все равно встраивать, то в случае обычных страниц все равно собирать скриптом и не быстро и не гибко. Тот же сборочный скрипт может и дать полностью кешированную страницу в структуру веб-сервера.
1.2 Если это аякс, то опять же что мешает аяксу дать страницу, которая будет частью конечной, статическую, кешированную, от веб-сервера.
2. Сможет — SSI.
Нет, я не спорю, я же не предлагаю «способ решение всех проблем» ;)
+дополнительный флаг о том что обновление кэша уже запущено
Причем он тоже обновляется, приводить к конфликтам и тоже внезапно может потянуть тяжелый бэкенд. Поэтому скрыто обновить кеш частями — тоже вариант.
На каждый ключ у вас фал — это очень плохая практика, забудьте ее, для таких веще лучше использовать спец БД, например bdb.
Так же у вас нет блокировок, и на GetData при двух конкурентных запросах может получится так, что в кеш единовременно положили данные два запроса, но запрос со старыми данными лег позднее чем с новыми — потеря данных.
highload.ru/papers2008/7158.html
просто смахивает на создание очередного велосипеда, не обижайтесь.
habrahabr.ru/blogs/webdev/42607/
habrahabr.ru/blogs/webdev/42972/
habrahabr.ru/blogs/webdev/43282/
habrahabr.ru/blogs/webdev/43540/
habrahabr.ru/blogs/webdev/43539/
habrahabr.ru/blogs/webdev/43735/
<?php
if(...) {
?>
html код
<?php
} else {
//здесь код php почему-то не распознается
}
?>
public function load ( $cache_name )
{
$cache_file = $this->cache_dir. DIRECTORY_SEPARATOR. $cache_name. '.php';
if ( filemtime ( $cache_file ) < time () )
{
$this->clear ( $cache_name );
return false;
}
if (! file_exists ( $cache_file ))
{
return $this->error = CACHE_FILE_NOT_EXISTS;
}
$content = @unserialize( @file_get_contents( $cache_file ));
if (! $content or! is_array ( $content ))
{
return false;
}
$this->debug[ 'load' ][ $cache_name ] ++;
return $content;
}
public function save ( $cache_name, $value, $expire = 0 )
{
$cache_file = $this->cache_dir. DIRECTORY_SEPARATOR. $cache_name. '.php';
@file_put_contents ( $cache_file, serialize( $value ), LOCK_EX );
if (! file_exists ( $cache_file ))
{
return false;
}
if ( $expire != 0 )
{
@touch( $cache_file, time () + $expire );
}
@chmod ( $cache_file, 0666 );
$this->debug[ 'save' ][ $cache_name ] ++;
}