В продолжение моей статьи про шаблоны, хочу рассказать про реализацию кэширования в Django. Основной упор будет сделан на кэширование частей шаблона – этот вопрос был затронут тут и послужил причиной написания этих двух статей. В предыдущей статье я слишком увлекся описанием самих шаблонов, так что постараюсь исправиться в этой.
Что такое кэширование, и какие оно дает преимущества под нагрузкой, думаю никому объяснять не надо. (Вступительные слова можно прочитать в начале соответствующего раздела документации по Django, ссылки в конце статьи.) Что можно кэшировать?
Остановимся подробнее на реализации этого в Django.
Настройка механизма кэширования в Django очень проста. В файле настроек проекта нужно указать переменную CACHE_BACKEND содержащую параметры хранения кэша: где, как долго и сколько. Поддерживаются следующие типы хранилищ:
Поддерживаются следующие параметры кэша:
timeout – время обновления кеша, в секундах;max_entries – максимальное количество записей в кэше;cull_frequency – процент старых записей, который удаляется по достижению max_entries.Синтаксис описан в документации, и повторять его нет смысла.
Тем, у кого есть свои сервера или возможность выделит пару гигабайт памяти под кэш, думаю, эта статья будет малоинтересна. Поэтому хранение в Memcached и локальной памяти я описывать не буду. Выбор между БД и ФС предоставлю читателю. Насколько быстрее тот или иной способ я не сравнивал. Для себя я выбрал ФС т.к. БД и так нагружена.
Кроме того, для удобства разработки, поддерживается еще "dummy" хранилище, которое на самом деле ничего не хранит.Думаю, будет уместно описать способ безболезненного объединения этих двух версий.
Основным файлом конфигурации проекта Django является settings.py. Я добавил еще два файла: settings_dev.py и settings_pub.py, а в settings.py написал:
import platform DEV_MODE=(platform.node()!='dpp.su') if DEV_MODE: DEBUG = True from settings_dev import * else: DEBUG = False from settings_pub import *
Функция platform.node возвращает имя компьютера, и я сравниваю его с именем своего сервера. Наверное, можно придумать более универсальное решение, но мне достаточно этого.
Таким образом, различающиеся настройки я выношу в эти файлы.
В нашем случае переменная CACHE_BACKEND в settings_dev.py будет равна 'dummy:///', а в settings_pub.py - 'file:///path/to/cache/'
Для кэширования всего сайта достаточно добавить 'django.middleware.cache.CacheMiddleware' в список MIDDLEWARE_CLASSES и переменную CACHE_MIDDLEWARE_SECONDS.
Кэшируются все страницы без параметров.
Подробнее в документации.
Для кэширования страницы используется функция cache_page:
from django.views.decorators.cache import cache_page def cache_this(request): ... cache_this = cache_page(cache_this, 60 * 15)
Также ее можно использовать в качестве декоратора:
@cache_page(60 * 15) def cache_this(request): ...
Параметр – время обновления кэша в секундах.
Для кэширования данных кэш Django предоставляет ожидаемые функции: set, get, delete и пару дополнительных. Настройки кэша берутся из переменной CACHE_BACKEND. Детали в документации.
Предыдущая часть статьи большей частью дублировала документацию Django – специально для того чтобы показать незнакомым с джангой, но знакомым с хабром, что кэширование в Django не требует от программиста никаких усилий.
Кэширование частей шаблона мне кажется наиболее удобным для использования. Для динамичных сайтов (читай «в общем случае») кэшировать всю страницу нельзя. А в шаблоне мы кэшируем только нужный нам участок.
Для начала в шаблоне необходимо подключить библиотеку тегов кэширования.
{% load cache %}
Тэг cache принимает два обязательных параметра: время обновления кэша (в секундах) и имя кэшируемого фрагмента.
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
Кроме того можно передать сколько угодно дополнительных параметров для хранения нескольких версий кэша. Примерами могут быть разные версии кэша в зависимости от имени пользователя или от номера страницы при постраничной разбивке данных внутри блока.
{% cache 500 sidebar request.user.username %}
.. sidebar for logged in user ..
{% endcache %}
{% cache 500 sidebar page %}
.. content varies by page parameter ..
{% endcache %}
Попробую показать наглядно, что при этом происходит, и какие дает нам преимущества.
Возьмем пример из предыдущей статьи. Для шаблона blog/tag_index.htm будет построено следующее дерево (соответствие цветов см. в предыдущей статье):
Что тут можно кэшировать? В первую очередь нужно кэшировать редко изменяющиеся данные, доставаемые из базы.
Если мы даем пользователю возможность изменить информацию в «подвале» сайта стоит ее кэшировать – изменяется она очень редко.
{% block footer %}
{% cache 5000 foot %}
{% block copyright %}
{% endblock %}
{% endcache %}
{% endblock %}
Посмотрев внимательнее на примеры шаблонов в предыдущей статье можно увидеть, что вторая колонка (sidebar) не меняется для всех страниц блога. Поскольку для ее заполнения выполняется довольно много запросов (у меня – список тегов, количество статей для каждого тега, список последних статей) ее стоит кэшировать.
{% block sidebar %}
{% cache 500 blog_sidebar %}
{% block tags %}
{% endblock %}
{% block recent %}
{% endblock %}
{% endcache %}
{% endblock %}
Список статей тоже делает довольно много запросов: нужно выбрать статьи в зависимости от страницы и для каждой статьи выбрать соответствующие ей теги.
{% block content %}
{{ tag.title }}
{{ tag.text }}
{% cache 500 article_list tag.id page %}
{{ block.super }}
{% endcache %}
{% endblock %}
Получим следующую картину:
Что происходит при запросе этой страницы?
При построении дерева для каждого тега вызываются конструирующие его функции, предоставляющие данные для отрисовки. Именно в этот момент подготавливаются запросы в базу. Непосредственно сами запросы происходят при обращении к данным по причине их ленивой природы в Django. Затем парсер шаблонов обегает дерево и для каждого блока вызывает функцию отрисовки. В случае кэширующего тега вложенные в него теги будут сконструированы и отрисованы только если кэш еще не создан или устарел.
Если не использовать теги (лениться «плодить» теги для одного запроса в базу), то все данные необходимо предоставить самому шаблону. Да, по причине ленивой природы запросов в Django, разница не очень существенная. Однако получается не очень логично – контроллер вроде как отработал, а результатами его работы никто не воспользовался. Даже можно писать кучу питоновского кода в expr-тегах внутри самого шаблона – кэшу все равно. Однако подобные «нарушения» архитектуры намного сложнее поддерживать. А уж если это писал другой человек... работая в веб-студии и поддерживая кучу сайтов, я в этом убедился.
Теперь комментарий к статье, которая послужила поводом написания всего этого.
dmmd, фактически Вы предлагаете зачаточный вариант того, что реализовано в Django: теги и ленивые запросы в базу. Суть остается той же, однако архитектура Django кажется мне намного более стройной, очевидной и, соответственно легче поддерживаемой.
Ссылки на официальную документацию:
Перепечатка и обсуждение на хабре.
Я начал переводить понемногу официальную документацию по Django, если кому интересно смотрите [тут] (http://apicom.org.ua/django/)
А кеширование - куда идет? в файлы ?
[q]
Поддерживаются следующие типы хранилищ:
- Memcached (в том числе на другой машине);
- база данных;
- файловая система;
- локальная память.
[/q]
Интересно написано Спасибо.