parser

Оператор ^cache[]

Misha v.3 [19 октября 2005]

«Кеширование». Это слово слышали многие, и некоторые из них даже догадываюся, что данные механизмы призваны ускорять процессы обработки данных.

Механизм кеширования позволяет не производить повторные вычисления, а брать уже готовый, заранее сохраненный результат.

Кеширование сложных страниц даже на несколько минут значительно снижает нагрузку на сервер, и при таком, краткосрочном кешировании можно не делать механизмов очистки кеша при внесении изменений через административный интерфейс. Но очевидно что долгосрочное кеширование + механизмы очистки кеша снизят нагрузку на сервер ещё больше.

В Parser 3 задачу кеширования строк выполняет оператор ^cache[файл](секунд){код}.

корневой auto.p:

@auto[]
$sRequest[$request:uri]
$sQuery[$request:query]

# определяем default значение для кеша — 3600 сек
$iDefaultCacheTime(3600)

^if(
#	был POST формы - отключаем кеширование
	$env:REQUEST_METHOD eq "POST"
#	пришли с локальных адресов - отключаем кеширование
#	удобно для разработки, чтобы разработчику не мешался кеш
	|| ^env:REMOTE_ADDR.match[^^192\.168][]
){
	$bDisableCache(1)
}{
	$bDisableCache(0)
}

^if($bDisableCache){
	$iTime(0)
#	определяем ключ кеширования-заглушку...
	$sKey[dummy]
}{
#	если кеширование не отключено - загружаем табличку с временами кеширования страниц
	$tCache[^table::load[/../data/_cache.cfg]]

#	пробегаем по таблице и смотрим, нет задано ли у нас специфическое время кеширования для запрошенной странички
#	hint: если строго зафиксировать формат данных в этом файле, то искать нужную запись с помощью ^locate[] или
#		преобразовывать в хэш и делать по нему хеш-лукап.
	^tCache.menu{
		^if(^sRequest.match[^^^trim[$tCache.url;/]][]){$iTime[$tCache.time]}
	}
	$iTime(^iTime.int($iDefaultCacheTime))

#	учитывая тот факт, что нам может придти ОЧЕНЬ длинный $request:query - "сворачиваем" его,
#	используя встроенный math:md5

#	табличка для замен в query, это пример. нужно из query выкинуть то, что мы считаем мусором
	$tRepl[^table::create[nameless]{?	&&^#0Atemp=aaa	&&}]

#	очищаем query от кусков, которые нам не нужны.
	$sQuery[^sQuery.replace[$tRepl]]
	$sQuery[^sQuery.match[&{2,}][g]{}]

#	нам для ключа нужно отдельно имя файла
	$tPart[^sRequest.split[?][hl]]

#	собственно формируем ключ кеширования: полный путь + закриптованная query
	$sKey[$tPart.0^if(def $sQuery){^math:md5[$sQuery]}]
}
#end @auto[]


@myСache[jBody]
# в $CACHE_DIR указан путь к каталогу, в котором хранятся данные кеша
^cache[$CACHE_DIR/$sKey]($iTime){
	$jBody
}
#end @myСache[]

Пример вызова, например из index.html:

@main[]
^myСache{
	^rem{ собственно код, формирующий страницу. при вызове метода не перепутайте... }
	^rem{ ...и не используйте случайно квадратные скобки вместо фигурных... }
}
#end @main[]

Если вы смотрели мои предыдущие примеры, то наверняка должны были заметить мой оператор ^Lib:location[...]:

@location[sTargetUrl]
^try{
	^cache(0)
}{
	$exception.handled(1)
}
...
$response:location[$sTargetUrl]
...

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

В этом случае я использую свой ^Lib:location[] вместо стандартного $response:location и уверен в том, что запрос с подобным урлом не сохранится в кеше (зачем он там?)

Написанный выше код несколько урезан и в нём есть неточности:

Следует заметить, что это всего лишь один из способов использования оператора ^cache[], и он далеко не единственный...

Не следует забывать, что содержимое кеша будет показываться посетителям до тех пор, пока не истечет его время жизни, при этом код внутри оператора cache выполнятся не будет. Если вам надо чтобы очистка кеша происходила по каким-то ведомым только вам законам — реализуйте её. Простейший способ — удаление всего содержимого кеша при любом изменении произведенном в вашем административном интерфейсе.

ВНИМАНИЕ! Пример писался теоретически на основании моего кода, поэтому в нем могут быть синтаксические или иные ошибки :).

Приведенный выше упрощенный способ кеширования html-страницы целиком работает в большинстве случаев, но все таки не всегда. Иногда при формировании тела страницы может потребоваться выставить определенные заголовки ответа броузеру (например $response:status[404] если посетитель пришел смотреть новость которой нет, или $response:last-modified[]). В этом случае всё будет работать хорошо только для тех обращений к серверу, которые не достаются из кеша, а создаются, т.к. оператор ^cache[] работает только с текстом страницы и ничего не знает про заголовки.

Чтобы решить поставленную задачу и кешировать страницу вместе с заголовками мной был написан класс CachePage, который по синтаксису очень похож на оператор cache, но кеширует ещё и заголовки ответа. При его использовании index.html из предыдущего примера выглядел бы следующим образом (метод @myСache[] становится не нужен):

@main[][oCachePage]
^use[CachePage.p]
$oCachePage[^CachePage::create[
	$.sCacheDir[$CACHE_DIR]
]]

^oCachePage.run[$sKey]($iTime){
	^rem{ собственно код, формирующий страницу. при вызове метода не перепутайте... }
	^rem{ ...и не используйте случайно квадратные скобки вместо фигурных... }
}
#end @main[]
Скачать:

CachePage.zip (02.04.2012  1,6 КБ)
Класс для кеширования страниц вместе с заголовками