Главная  /  Правка версии 1  /  Правка версии 2  /   /  Область пользователей

Отличия "Усложненное кэширование" вер. 1 сравн. вер. 2

Содержимое:

## Введение
В отличие от многих существующих сегодня систем кэширования в различных веб-фреймворках на основе понятий "ключ - значение - тайм-аут", кэш CppCMS представляет собой модель "ключ - триггеры - значение - тайм-аут", обеспечивющую простой и эффективный способ инвалидации кэша - держать его согласованным.
В отличие от многих существующих сегодня систем кэширования в различных веб-фреймворках на основе понятий "ключ - значение - тайм-аут", кэш CppCMS представляет собой модель "ключ - триггеры - значение - тайм-аут", обеспечивающую простой и эффективный способ инвалидации кэша - держать его согласованным.
У каждого закэшированного объекта есть следующие свойства:
1. Уникальный ключ, который идентифицирует его.
2. Время жизни элемента, которое может быть бесконечным
1. Уникальный ключ, который его идентифицирует.
2. Время жизни элемента, которое может быть бесконечным.
3. Набор триггеров, которые могут инвалидировать кэш.
Например, у нас есть следующий набор триггеров:
![Триггеры](/pics/triggers-scheme.png)
Т.о. мы можем инвалидировать несколько ключей "воскрешением" различных триггеров.
Например, когда мы обновляем статью о `drugs` мы
"воскрешаем" триггер `new_drugs` и инвалидируем `health`-новости и `news_all`, зависящие от данного триггера.
С другой стороны, новости "technology" остались нетронутыми.
Этот метод позволяет сохранить веб-сайт и актуальным и полностью кэшируемым.
## Управление триггерами
### Прямая обработка
При кэшировании страницы Вы можете легко добавить к ней триггер, используя функцию `cppcms::cache_interface::add_trigger()`.
Например:
if(cache().fetch_page("my_page"))
return;
// Создаем некоторый контент
cache().add_trigger("my_trigger");
cache().store_page("my_page");
Теперь объект `my_page` будет зависеть от двух триггеров
`my_trigger` и собственного `my_page`.
С очисткой кэша "воскрешением" триггера:
С очисткой кэша, "воскрешением" триггера:
cache().rise("my_trigger");
будет создан кэш для всех зависящих от него объектов, включая `my_page`.
Если Вы используете нтерфейс кэширования для хранения некоторых объектов или текстовых фреймов в нем, можно передать набор триггеров непосредственно как параметр.
Если Вы используете интерфейс кэширования для хранения некоторых объектов или текстовых фреймов в нем, можно передать набор триггеров непосредственно как параметр.
std::set<std::string> my_triggers;
my_triggers.insert("my_trigger");
cache().store_data("my_object",my_object,my_triggers);
_Замечание:_ триггер `my_object` будет автоматически добавлен к ключу `my_object`.
### Автоматическое Отслеживание Зависимостей
Кэш CppCMS позволяет автоматически отслеживать зависимости.
Предположим, есть следующий код:
if(cache().fetch_page("foo"))
return;
if(!cache().fetch_data("settings",settings)) {
load_settings(settings);
cache().store_data("settings",settings);
}
// делаем что-нибудь
cache().store_page("foo");
Теперь мы используем объект "setting" для создания страницы
"foo". При вызове `fetch_data()` или `store_data()` триггер "settings" добавляется как зависимый от "foo".
Более того, если у "settings" есть собственные триггеры:
if(!cache().fetch_data("settings",settings)) {
load_settings(settings);
std::set<std::string> settings_triggers;
settings_triggers.insert("bar");
cache().store_data("settings",settings);
}
, то страница "foo" будет автоматически зависеть от триггеров "settings" и "bar" и т.о., например, "воксрешение" "bar" инвалидирует и "foo" и "settings".
Автоматическая запись может быть отключена установкой параметра `no_triggers` в "true" для `store_data()`
и "fetch_data()", например:
if(!cache().fetch_data("settings",settings,true)) {
load_settings(settings);
cache().store_data("settings",settings,-1,true);
// -1 для бесконечного тайм-аута
}
### Отслеживание вложенностей
Триггеры - регистратор для фреймов, но иногда требуется регистрировать события триггеров на уровнях ниже. Например:
Триггеры - регистратор (recorder) для фреймов, но иногда требуется регистрировать события триггеров на уровнях ниже. Например:
- "page"
- "settings"
- "local_settings"
- "global_settings"
Типа:
if(cache().fetch_page("foo"))
return;
if(!cache().fetch_data("settings",settings)) {
if(!cache().fetch_data("local_settings",ls) {
...
}
if(!cache().fetch_data("global_settings",gs) {
...
}
load_settings(settings,ls,gs)
cache().store_data("settings",settings);
}
cache().store_page("foo");
Но триггеры `local_settings` и `global_settings` не будут регистрировать события для "settings", потому что функция "store\_data()" не знает о них.
Потому, для обработки такой ситуации есть объект [triggers\_recorder](/cppcms_ref/1.0.2/classcppcms_1_1triggers__recorder.html):
Изменим наш код следующим образом:
if(!cache().fetch_data("settings",settings)) {
cppcms::triggers_recorder rec(cache());
if(!cache().fetch_data("local_settings",ls) {
...
}
if(!cache().fetch_data("global_settings",gs) {
...
}
load_settings(settings,ls,gs);
cache().store_data("settings",settings,rec.detach());
}
Запускаем экземпляр `triggers_recorder`
и в конце раздела регистрируем события триггеров на их отсоединение вызовом метода `detach()`, возвращающего набор триггеров, в данном случае `local_settings` и `global_settings`.
Мы получили все, что требовалось.
## Запись Текстовых Фреймов
### Из Приложения
Представьте ситуацию, когда некоторая часть
HTML-фрейма является общей для многих страниц
и требует каких-то нетривиальных методов
для их создания (например, сделать какие-то сложные запросы
к БД, чтобы получить все нужные нам данные).
CppCMS предоставляет простой [copy_filter](/cppcms_ref/1.0.2/classcppcms_1_1copy__filter.html).
Он используется следующим образом:
std::string frame;
if(cache().fetch_frame("key",frame)) {
response().out() << frame;
}
else {
cppcms::copy_filter tee(response().out());
...
// генерируем что-то тяжелое
...
cache().store_frame("key",tee.detach());
}
_Замечание:_ Если фрейм уже существует в кэше, мы
записываем его напрямую в вывод, но если
нет - создаем его как обычно,
но с добавлением небольшого фильтра `tee` на поток вывода, который будет копировать все данные
во временный буфер и будет также записывать их в поток.
При вызове `tee.detach()` мы удалим фильтр и вернем весь записанный фрейм.
### Из Представления (view)
Гораздо более естественно кэшировать некоторые фреймы из представлений (view) - движком темплейтов. CppCMS предоставляет такой инструмент: тэг [`<% cache ... %>`](/wikipp/en/page/cppcms_1x_templates_flow#Caching.Elements).
Например:
<% template foo() %>
<html>
...
<body>
<% cache "sidebar" %>
<div id="sidebar">
...
</div>
<% end cache %>
...
</body>
</html>
<% end template>
Однако, часто недостаточно "просто кэшировать"
какой-то HTML фрейм, обычно нам необходимо подготовить какие-то данные для рендеринга, потому хотелось бы
иметь возможность сделать что-то вроде обновления контента рендеринга с некоторыми данными из базы данных в случае промаха кэша.
иметь возможность сделать что-то вроде обновления контента рендеринга с некоторыми данными из базы данных, в случае промаха кэша.
Мы можем добавить callback на контент, который будет вызываться в случае промаха кэша.
<% cache "sidebar" on miss update_content() %>
<div id="sidebar">
...
</div>
<% end cache %>
Т.о. мы добавили контент, который рендерит callback `update_content()`, который может выглядить как:
Т.о. мы добавили контент, который рендерит callback `update_content()`, который может выглядеть как:
my_content : public cppcms::base_content {
booster::function<void()> update_content;
};
И в приложении определим его как:
my_content c;
// соединяем метод `update_content` и callback
c.update_content = std::bind(&my_app::update_content,this,&c);
И наша функция обновления будет выглядеть так:
void update_content(my_content *c)
{
c->some_data = fetch_some_bid_data_from_db();
}
Т.о. при промахе кэша будет вызван `update_content` и настройка всего контента для рендеринга этого фрейма.
## Замечание о кэшировании на уровне страницы
Кэширование на уровне страницы - лучшее кэширование, которое может быть сделано, если Вы всегда можете кэшировать всю страницу, а не, например, объекты базы данных, которые рендерятся в представлении (view).
Для этого есть две причины:
1. Сериализация и десериализация объектов занимает много времени. Даже простой рендеринг отнимает время. Потому кэширование всей страницы даст лучшую производительность.
2. Страницы CppCMS кэшируются уже с gzip-сжатием. Потому Вы не только экономите время, необходимое для сжатия страниц, но и уменьшаете время отклика, из-за данных, отправляемых
гораздо меньшими, чем оригинальный HTML.
Спасибо тому факту, что _все_ современные браузеры
поддерживают gzip-сжатие, делая кэширование на уровне
страницы быстрым и эффективным.
## Триггеры или Тайм-ауты
CppCMS предоставляет два способа управления временем жизни кэшированных объектов: путем ограничения их времени жизни через тайм-аут или через явное удаление их из кэша с помощью триггеров.
Если какая-то страница обновляется и посещается очень часто, то возможно, стоит установить небольшой тайм-аут и никогда не инвалидировать его вручную.
Если какая-то страница обновляется и посещается очень часто, то возможно, стоит установить небольшой тайм-аут и никогда не инвалидировать ее вручную.
Однако, если Вы можете определить источники инвалидации страницы, осторожно конструируйте набор триггеров и прозрачнее определяйте зависимости, чтобы иметь возможность почти никогда не использовать таймауты и сохранять актуальность веб-сайта.
Например, система кэширования блога CppCMS полностью основана на триггерах и никогда не использует тайм-ауты. С другой стороны, эта wiki использует триггеры для одних страниц и таймауты - для других, как например, индекс, который при каждом обновлении страницы будет вызывать ее инвалидацию.
В итоге, наиболее безопасно использовать оба метода. Так, даже если у Вас баг и отсутствует какая-то зависимость, страница обновится по событию.

Проект

CppCMS является основой веб-разработки для выполнения ресурсоемких приложений.

Размещение

SourceForge.net Logo

Поддержать проект

CppCMS needs You


Навигация

Главная страница


Валидация CSS | Валидация XHTML 1.0