Pull to refresh

Использование Nginx в качестве сборщика в трудном деле кэширования

Reading time3 min
Views1.2K
Зачастую мы не кэшируем вывод только из-за того, что среди данных, которые можно легко и безболезненно закэшировать, есть часто меняющаяся информация (привязанная, как правило, к конкретному пользователю).
Решений у этой проблемы несколько, но хороших существенно меньше

Решения


Решений у этой проблемы несколько
1. Кэшировать только выборки.
Делать это можно и нужно, но все равно совершаются лишние телодвижения, вроде разбора URL, инициации моделей и вывода данных в шаблон. Все это увеличивает энтропию и ведет к преждевременной гибели вселенной.
2. Собирать данные с помощью JavaScript.
С моей точки зрения, эта практика порочна, так как ведет к потере данных у клиентов, не поддерживающих javascript. Возгласы о том, что приличные клиенты держат javascript включенным, я с гневом отметаю, так как в мире существуют мобильные браузеры, читалки для слепых и просто трудолюбивые роботы. Это решение применимо (и даже желательно) исключительно для вывода различных «информеров», без которых можно легко обойтись.
Таким образом, данные мы должны собирать еще на сервере, причем максимально легким способом. В данном случае применяется заслуженно всеми любимый сервер nginx и незаслуженно забытая технология ssi.

Сразу оговорюсь, что великое открытие принадлежит не мне, а было рождено в недрах уважаемой компании mail.ru, специалисты которой поделились им еще на прошлом Highload.
Однако их решение было, по моему мнению, слишком громоздким и несколько отличалось по идеологии. Фактически там nginx использовался в качестве шаблонизатора с поддержкой циклов, условных переходов и прочих финтифлюшек, которые для «маленького легкого фронтэнда» кажутся немного излишними. Кроме того, вопреки радостным заверениям авторов, проект в открытом доступе так и не появился, а писать такое великолепие в мои планы не входило.

Идея



Смысл состоит в том, чтобы разделить хорошо кэшируемые данные и данные, которые кэшируются плохо или не кэшируются вообще, последние, как правило, привязаны к пользователю.
Например, есть список сообществ, рядом с каждым в зависимости от того вступил ли туда пользователь (и авторизован ли он вообще), выводиться кнопка «вступить» или кнопка «покинуть».

В итоге мы создаем кэш в котором храниться уже готовая html страница, содержащая необходимые данные и ssi инструкции для вывода переменных, с некэшируемыми данными. Также создается «бэкэнд» файл, который генерирует эти переменные.

1. Запускаем кэш по нормализованному URL, если кэш с нормализованным URL уже есть- перенаправляем на него, если нет- создаем фейковый кэш, обновляющий страницу с некоторой задержкой- т.е. «лочим» страницу.
/communities/new/1.html — первая страница списка сообществ отсортированных по времени создания
2. Делаем выборку хорошо кэшируюмых данных.
В нашем случае- самые новые 10 сообществ.
3. Генерим и «запоминаем» код для выборки некэшируемых данных.
Т.е. в каких сообществах из выбранных состоит текущий пользователь.
4. Выводим список. Смысл в том, что на уровне инструкций шаблонизатора мы имеем возможность указать участки кода, которые создаются в бэкэнде. Таким образом код «запоминается» и дополняется кодом генерации ssi переменные , а po:ssi заменяется на ssi инструкцию .
Выглядит это приблизительно так (используются шаблонизатор конструкции велосипед, потаенный смысл его создания описан тут: cloud.habrahabr.ru/blog/22445):


{$Title}

<?
if (auth::is_logged_id()){
if (isset($memberof['{$id}'])&&$memberof['{$id}']){
?>покинуть сообщество<?
}
else {
?>покинуть сообщество<?
}
}
?>





5. Сохраняем итог выполнения скрипта в html файл, а код «привязанный к пользователю» в бэкэнд файл. К кэшу файла добавляем инструкцию ssi include virtual, подключающую бэкэнд файл.

Таким образом, при получении данных из кэша, происходит следующее:

1. nginx получает запрос и находит страницу в кэше.
2. Выполняет инструкцию include virtual (тут важно не забыть выставить параметр wait), по которой подключает сгенерированный backend файл. Там определяется текущий пользователь и его членство в выводимых на этой странице сообществах, создается html код кнопок «вступить» и «покинуть», который записывается в соответствующие ssi переменные.
3. Переменные выводятся в соответствующих местах шаблонов, в результате чего получаем требуемую страничку.

Актуальность кэша.



Актуальность кэша поддерживается за счет «имен выборок», т.е. каждая выборка является объектом, которая имеет одно или несколько имен, как правило, имя таблицы и/или имя таблицы и ключ.
Например, выборка сообществ из таблицы «communities» с тематикой «sport» имеет имя communities_sport. Имена выборок выставляются на уровне модели, и на уровне моделей может быть произведен сброс кэша при UPDATE/DELETE/CREATE.

В итоге при сохранении кэша мы сохраняем связи страницы с именами всех проведенных выборок и при изменении удаляем все страницы с данным именем.
Tags:
Hubs:
+5
Comments8

Articles