понедельник, 7 сентября 2009 г.

Кэширование вывода в AOL Server

Для кэширования статичного контента в AOL Server можно использовать такую технику:

rename ::html::menu ::html::menu_orig
proc ::html::menu {current} {
with-adp-cache mainmenu,$current [list ::html::menu_orig $current]
}


Здесь мы оборачиваем функцию ::html::menu в кэширующий блок, переименовывая оригинальную функцию.

Сама реализация кэширующей функции может быть такой:

proc with-adp-cache {key script} { 
if {[info procs ns_adp_puts] ne {}} {
uplevel 1 $script
return
}
if {[nsv_exists ns_adp_cache $key] == 1} {
ns_adp_puts [nsv_get ns_adp_cache $key]
return
}
set result [with-adp-return $script]
if {[nsv_exists ns_adp_cache $key] == 0} {
nsv_set ns_adp_cache $key $result
}
ns_adp_puts $result
}

proc with-adp-return {script} {
if {[info commands ns_adp_puts_orig] eq {}} {
rename ns_adp_puts ns_adp_puts_orig
rename _ns_adp_puts ns_adp_puts
}
uplevel 1 $script
if {[info procs ns_adp_puts] ne {}} {
rename ns_adp_puts _ns_adp_puts
rename ns_adp_puts_orig ns_adp_puts
}
set _ns_adp_buffer $::_ns_adp_buffer
unset ::_ns_adp_buffer
return $_ns_adp_buffer
}

proc _ns_adp_puts {args} {
global _ns_adp_buffer
if {[llength $args] == 1} {
append _ns_adp_buffer [lindex $args 0]\n
} elseif {[llength $args] == 2 && [lindex $args 0] eq {-nonewline}} {
append _ns_adp_buffer [lindex $args 1]
} else {
return -code error "Unknown params to ns_adp_puts hook function: [info level 0]"
}
}


Собственно, я использую несколько иную реализацию (у меня ns_adp_puts/ns_adp_append обернуты в более высокоуровневую функцию, которая и реализует кэширование вывода при необходимости), но идея та же - использование переменной-буфера. В AOL Server можно обратиться к встроенному буферу, но насколько такой вариант будет эффективнее, оценить не берусь. Плюс к тому придется делать отсылку содержимого буфера при старте кэширующей функции, что так же далеко не всегда желательно. Интересно, предложит ли кто более другой вариант?

Обсуждение здесь:
cache function for adp pages output

Upd.

В настоящее время использую следующую реализацию:


############################################
# Copyright 2009, Mobile Business Group
############################################

namespace eval ::ns_html:: {}

# функция кэширует и отсылает клиенту результат выполнения кода script
# при повторном вызове блока кода с таким же key функция вернет результат из кэша
# как пример, см. ::html::menu - результат выполнения этой функции зависит только от
# текущего выбранного пункта меню, количество которых ограничено несколькими десятками
proc ::ns_html::cache {key script} {
global _ns_html_buffer
# кэш уже существует, возвращаем его и отменяем выполнение скрипта
# также проверяем на вложенный вызов кэша - должен игнорироваться
if {[info exists _ns_html_buffer] == 0 && [nsv_exists ns_html_cache $key] == 1} {
ns_log Debug "get_CACHED $key"
ns_html::puts [nsv_get ns_html_cache $key]
return
}
# активировать кэширование, если оно еще не было активировано, иначе просто выполнить тело скрипта
if {[info exists _ns_html_buffer] == 0} {
ns_log Debug "set_CACHE $key"
# при существующем буфере ns_html::puts будет сохранять контент в буфер вместо отправки клиенту
set _ns_html_buffer {}
if {[catch {uplevel 1 $script} errmgs]} {
return -code error $errmsg
unset _ns_html_buffer
}
# все результаты выведены в буффер
nsv_set ns_html_cache $key $_ns_html_buffer
# после удаления буфера ns_html::puts отправит контент клиенту
unset _ns_html_buffer
ns_html::puts [nsv_get ns_html_cache $key]
} else {
ns_log Debug "ignore_CACHE $key"
uplevel 1 $script
}
}


Пример:

ns_html cache testpage {
ns_html html ! {
ns_html head ! {
ns_html title - Test
}
ns_html body ! {
ns_html pre ! {
ns_html puts {EN:Page content.} \n {RU:Содержимое страницы} \n
}
}
}
}

Комментариев нет:


(C) Alexey Pechnikov aka MBG, mobigroup.ru