Фреймворк кеширования Django ¶
Фундаментальный компромисс динамических веб-сайтов в том, что они динамические. Каждый раз, когда пользователь запрашивает страницу, веб-сервер выполняет всевозможные вычисления - от запросов к базе данных до рендеринга шаблонов и бизнес-логики - для создания страницы, которую видит посетитель вашего сайта. Это намного дороже с точки зрения накладных расходов на обработку, чем ваша стандартная конфигурация сервера чтения файла вне файловой системы.
Для большинства веб-приложений эти накладные расходы не имеют большого значения. Большинство веб-приложений не являются washingtonpost.com
или slashdot.org
; это сайты малого и среднего размера с средним трафиком. Но для сайтов со средней и высокой посещаемостью важно максимально сократить накладные расходы.
Вот тут и пригодится кеширование.
Кэшировать что-либо - значит сохранить результат дорогостоящих вычислений, чтобы вам не пришлось выполнять вычисления в следующий раз. Вот псевдокод, объясняющий, как это будет работать для динамически генерируемой веб-страницы:
given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page
Django поставляется с надежной системой кеширования, которая позволяет сохранять динамические страницы, поэтому их не нужно вычислять для каждого запроса. Для удобства Django предлагает разные уровни детализации кеширования: вы можете кэшировать вывод определенных представлений, вы можете кэшировать только те части, которые сложно создать, или вы можете кэшировать весь свой сайт.
Django также хорошо работает с «нисходящими» кэшами, такими как Squid, и кешами на основе браузера. Это типы кешей, которые вы не контролируете напрямую, но которым вы можете предоставить подсказки (через HTTP-заголовки) о том, какие части вашего сайта и как следует кэшировать.
Смотрите также
Философия дизайна Cache Framework объясняет некоторые из проектных решений каркаса.
Настройка кеша ¶
Система кеширования требует небольшой настройки. А именно, вы должны указать ему, где должны находиться ваши кэшированные данные - будь то в базе данных, в файловой системе или непосредственно в памяти. Это важное решение, которое влияет на производительность вашего кеша; да, некоторые типы кешей быстрее других.
Ваши предпочтения кеширования находятся в CACHES
настройках вашего файла настроек. Вот объяснение всех доступных значений для
CACHES
.
Memcached ¶
Memcached - это самый быстрый и наиболее эффективный тип кеша, изначально поддерживаемый Django. Это полностью основанный на памяти кэш-сервер, изначально разработанный для обработки высоких нагрузок на LiveJournal.com, а затем открытый Danga Interactive. Он используется такими сайтами, как Facebook и Wikipedia, для уменьшения доступа к базе данных и значительного повышения производительности сайта.
Memcached работает как демон, и ему выделяется определенный объем оперативной памяти. Все, что он делает, - это быстрый интерфейс для добавления, извлечения и удаления данных в кеше. Все данные хранятся непосредственно в памяти, поэтому нет накладных расходов на использование базы данных или файловой системы.
После установки самого Memcached вам необходимо установить привязку Memcached. Доступно несколько привязок Python Memcached; два, поддерживаемые Django, - это pylibmc и pymemcache .
Чтобы использовать Memcached с Django:
- Установите
BACKEND
наdjango.core.cache.backends.memcached.PyMemcacheCache
илиdjango.core.cache.backends.memcached.PyLibMCCache
( в зависимости от выбранного Memcached связывания) - Набор
LOCATION
дляip:port
значений, гдеip
это IP - адрес Memcached демон иport
это порт , на котором работает Memcached, или кunix:path
значению, гдеpath
это путь к файлу сокета Memcached Unix.
В этом примере Memcached работает на локальном (127.0.0.1) порту 11211 с использованием pymemcache
привязки:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
}
}
В этом примере Memcached доступен через локальный файл сокета Unix
/tmp/memcached.sock
с использованием pymemcache
привязки:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}
Отличной особенностью Memcached является возможность совместного использования кеша на нескольких серверах. Это означает, что вы можете запускать демоны Memcached на нескольких машинах, и программа будет рассматривать группу машин как единый
кеш, без необходимости дублировать значения кеша на каждой машине. Чтобы воспользоваться этой функцией, включите все адреса серверов в
LOCATION
виде строки с запятой или через запятую, либо в виде списка.
В этом примере кеш совместно используется экземплярами Memcached, работающими на IP-адресах 172.19.26.240 и 172.19.26.242, оба на порте 11211:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
В следующем примере кеш совместно используется экземплярами Memcached, работающими на IP-адресах 172.19.26.240 (порт 11211), 172.19.26.242 (порт 11212) и 172.19.26.244 (порт 11213):
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11212',
'172.19.26.244:11213',
]
}
}
Последний момент, касающийся Memcached, заключается в том, что кэширование на основе памяти имеет недостаток: поскольку кэшированные данные хранятся в памяти, данные будут потеряны, если ваш сервер выйдет из строя. Очевидно, что память не предназначена для постоянного хранения данных, поэтому не полагайтесь на кэширование на основе памяти в качестве единственного хранилища данных. Без сомнения, ни один из бэкэндов кэширования Django не должен использоваться для постоянного хранения - все они предназначены для решения кэширования, а не хранения - но мы указываем на это здесь, потому что кэширование на основе памяти является особенно временным.
PyMemcacheCache
Бэкенд был добавлен.
Не рекомендуется с версии 3.2: MemcachedCache
Бэкенд нежелателен python-memcached
имеют некоторые проблемы и , кажется, никто не поддерживались. Используйте PyMemcacheCache
или
PyLibMCCache
вместо.
Кеширование базы данных ¶
Django может хранить свои кэшированные данные в вашей базе данных. Это лучше всего работает, если у вас есть быстрый, хорошо проиндексированный сервер базы данных.
Чтобы использовать таблицу базы данных в качестве бэкэнда кеша:
- Установить
BACKEND
наdjango.core.cache.backends.db.DatabaseCache
- Установите
LOCATION
значениеtablename
, имя таблицы базы данных. Это имя может быть любым, если это действительное имя таблицы, которое еще не используется в вашей базе данных.
В этом примере имя таблицы кеша my_cache_table
:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
}
}
Создание таблицы кеширования ¶
Перед использованием кеша базы данных вы должны создать таблицу кеша с помощью этой команды:
python manage.py createcachetable
Это создает таблицу в вашей базе данных, которая имеет правильный формат, который ожидает система кеширования базы данных Django. Название таблицы взято из
LOCATION
.
Если вы используете несколько кешей базы данных, createcachetable
создает по одной таблице для каждого кеша.
Если вы используете несколько баз данных, createcachetable
обратите внимание на
allow_migrate()
метод маршрутизаторов вашей базы данных (см. Ниже).
Мол migrate
, createcachetable
не трогаем существующий стол. Это только создаст недостающие таблицы.
Чтобы распечатать SQL, который будет запускаться, а не запускать его, используйте
опцию.createcachetable --dry-run
Несколько баз данных ¶
Если вы используете кэширование базы данных с несколькими базами данных, вам также необходимо настроить инструкции маршрутизации для таблицы кеширования вашей базы данных. Для целей маршрутизации таблица кэша базы данных отображается как модель с именем
CacheEntry
в приложении с именем django_cache
. Эта модель не будет отображаться в кэше моделей, но детали модели могут использоваться для целей маршрутизации.
Например, следующий маршрутизатор будет направлять все операции чтения из кэша cache_replica
и все операции записи в
cache_primary
. Таблица кеша будет синхронизироваться только на
cache_primary
:
class CacheRouter:
"""A router to control all database cache operations"""
def db_for_read(self, model, **hints):
"All cache read operations go to the replica"
if model._meta.app_label == 'django_cache':
return 'cache_replica'
return None
def db_for_write(self, model, **hints):
"All cache write operations go to primary"
if model._meta.app_label == 'django_cache':
return 'cache_primary'
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"Only install the cache model on primary"
if app_label == 'django_cache':
return db == 'cache_primary'
return None
Если вы не укажете направления маршрутизации для модели кэша базы данных, серверная часть кеша будет использовать эту default
базу данных.
И если вы не используете бэкэнд кеша базы данных, вам не нужно беспокоиться о предоставлении инструкций по маршрутизации для модели кеширования базы данных.
Кеширование файловой системы ¶
Бэкэнд на основе файлов сериализует и сохраняет каждое значение кэша как отдельный файл. Для того, чтобы использовать этот бэкэнд набор BACKEND
для
"django.core.cache.backends.filebased.FileBasedCache"
и
LOCATION
в нужный каталог. Например, для хранения кэшированных данных /var/tmp/django_cache
используйте этот параметр:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}
Если вы работаете в Windows, поместите букву диска в начало пути, например:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': 'c:/foo/bar',
}
}
Путь к каталогу должен быть абсолютным, то есть он должен начинаться с корня вашей файловой системы. Неважно, поставили ли вы косую черту в конце настройки.
Убедитесь, что каталог, на который указывает этот параметр, либо существует и доступен для чтения и записи, либо он может быть создан пользователем системы, под которым работает ваш веб-сервер. Продолжая приведенный выше пример, если ваш сервер работает от имени пользователя apache
, убедитесь, что каталог /var/tmp/django_cache
существует и доступен для чтения и записи пользователем apache
или что он может быть создан пользователем apache
.
Предупреждение
Когда кеш LOCATION
содержится внутри
MEDIA_ROOT
, STATIC_ROOT
или
STATICFILES_FINDERS
, могут быть раскрыты конфиденциальные данные.
Злоумышленник, получивший доступ к файлу кеша, может не только фальсифицировать HTML-контент, которому ваш сайт будет доверять, но и удаленно выполнить произвольный код, поскольку данные сериализуются с использованием pickle
.
Кэширование в локальной памяти ¶
Это кеш по умолчанию, если другой не указан в вашем файле настроек. Если вам нужны преимущества скорости кэширования в памяти, но у вас нет возможности запускать Memcached, рассмотрите возможность использования бэкэнда кеширования в локальной памяти. Этот кеш предназначен для каждого процесса (см. Ниже) и ориентирован на потоки. Чтобы использовать его, установите BACKEND
значение "django.core.cache.backends.locmem.LocMemCache"
. Например:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
Кэш LOCATION
используется для идентификации отдельных хранилищ памяти. Если у вас только один locmem
кеш, вы можете опустить
LOCATION
; однако, если у вас более одного кэша локальной памяти, вам нужно будет присвоить имя хотя бы одному из них, чтобы они были разделены.
Кэш использует стратегию отбраковки наименее недавно использовавшихся (LRU).
Обратите внимание, что каждый процесс будет иметь свой собственный частный экземпляр кеша, что означает невозможность межпроцессного кэширования. Это также означает, что кэш локальной памяти не особенно эффективен с точки зрения памяти, поэтому, вероятно, это не лучший выбор для производственных сред. Это хорошо для развития.
Фиктивное кеширование (для разработки) ¶
Наконец, Django поставляется с «фиктивным» кешем, который на самом деле не кеширует - он просто реализует интерфейс кеша, ничего не делая.
Это полезно, если у вас есть производственный сайт, который использует усиленное кэширование в различных местах, но в среде разработки / тестирования, где вы не хотите кэшировать и не хотите менять свой код на особый случай последнего. Чтобы активировать фиктивное кеширование, установите следующее BACKEND
:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
Использование собственного бэкэнда кеширования ¶
Хотя Django включает в себя готовую поддержку ряда серверных модулей кеширования, иногда вам может потребоваться использовать настраиваемый серверный модуль кеширования. Чтобы использовать внешний кеш-сервер с Django, используйте путь импорта Python в
BACKEND
качестве CACHES
параметра, например:
CACHES = {
'default': {
'BACKEND': 'path.to.backend',
}
}
Если вы создаете собственный бэкэнд, вы можете использовать стандартные бэкэнды кеширования в качестве эталонных реализаций. Вы найдете код в
django/core/cache/backends/
каталоге исходного кода Django.
Примечание: без действительно веских причин, таких как хост, который их не поддерживает, вам следует придерживаться бэкэндов кеширования, включенных в Django. Они хорошо протестированы и хорошо задокументированы.
Кэшировать аргументы ¶
Каждому бэкэнду кеширования можно дать дополнительные аргументы для управления поведением кеширования. Эти аргументы предоставляются в качестве дополнительных ключей в
CACHES
настройке. Допустимые аргументы:
TIMEOUT
: Тайм-аут по умолчанию в секундах, используемый для кеширования. По умолчанию этот аргумент равен300
секундам (5 минутам). Вы можете установитьTIMEOUT
значение,None
чтобы по умолчанию ключи кеша никогда не истекали. Значение0
заставляет ключи немедленно истекать (фактически «не кэшировать»).OPTIONS
: Любые параметры, которые должны быть переданы в серверную часть кеша. Список допустимых параметров будет варьироваться в зависимости от серверной части, а серверные части кеширования, поддерживаемые сторонней библиотекой, будут передавать свои параметры непосредственно в базовую библиотеку кеша.Кэш - движки , которые реализуют свою собственную стратегию отбрасывания невидимых граней (то есть
locmem
,filesystem
иdatabase
бэкэндов) будет выполнять следующие функции:MAX_ENTRIES
: Максимальное количество записей в кэше до удаления старых значений. По умолчанию этот аргумент равен300
.CULL_FREQUENCY
: Доля записей, которые отбрасываются приMAX_ENTRIES
достижении. Фактическое соотношение установлено таким образом, чтобы при достижении значения отбрасывать половину записей . Этот аргумент должен быть целым числом по умолчанию .1 / CULL_FREQUENCY
CULL_FREQUENCY
2
MAX_ENTRIES
3
Значение
0
forCULL_FREQUENCY
означает, что весь кеш будет сброшен приMAX_ENTRIES
достижении. На некоторых бэкэндах (database
в частности) это значительно ускоряет отсечение за счет большего количества промахов в кеше.
Серверные модули Memcached передают содержимое
OPTIONS
аргументов в виде ключевых слов конструкторам клиента, что позволяет более эффективно управлять поведением клиента. Пример использования см. Ниже.KEY_PREFIX
: Строка, которая будет автоматически добавлена (добавлена по умолчанию) ко всем ключам кеша, используемым сервером Django.См. Документацию по кешу для получения дополнительной информации.
VERSION
: Номер версии по умолчанию для ключей кеша, сгенерированных сервером Django.См. Документацию по кешу для получения дополнительной информации.
KEY_FUNCTION
Строка, содержащая пунктирный путь к функции, которая определяет, как составить префикс, версию и ключ в окончательный ключ кеша.См. Документацию по кешу для получения дополнительной информации.
В этом примере серверная часть файловой системы настраивается с таймаутом 60 секунд и максимальной емкостью 1000 элементов:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
'TIMEOUT': 60,
'OPTIONS': {
'MAX_ENTRIES': 1000
}
}
}
Вот пример конфигурации pylibmc
базового сервера, который включает двоичный протокол, аутентификацию SASL и ketama
режим поведения:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '127.0.0.1:11211',
'OPTIONS': {
'binary': True,
'username': 'user',
'password': 'pass',
'behaviors': {
'ketama': True,
}
}
}
}
Вот пример конфигурации pymemcache
бэкэнда на основе, который включает пул клиентов (что может повысить производительность, поддерживая подключение клиентов), обрабатывает ошибки кэша памяти / сети как промахи кеша и устанавливает TCP_NODELAY
флаг на сокете соединения:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
'OPTIONS': {
'no_delay': True,
'ignore_exc': True,
'max_pool_size': 4,
'use_pooling': True,
}
}
}
Кеш для каждого сайта ¶
После настройки кеша самый простой способ использовать кеширование - это кэшировать весь ваш сайт. Вам нужно будет добавить
'django.middleware.cache.UpdateCacheMiddleware'
и
'django.middleware.cache.FetchFromCacheMiddleware'
в свою
MIDDLEWARE
настройку, как в этом примере:
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]
Примечание
Нет, это не опечатка: промежуточное ПО «update» должно быть первым в списке, а промежуточное ПО «fetch» должно быть последним. Детали немного неясны, но, если вам нужна полная история , см. Порядок ПОСТОЯННОГО ОБЕСПЕЧЕНИЯ ниже.
Затем добавьте следующие необходимые настройки в файл настроек Django:
CACHE_MIDDLEWARE_ALIAS
- Псевдоним кеша, используемый для хранения.CACHE_MIDDLEWARE_SECONDS
- Количество секунд, в течение которых каждая страница должна быть кэширована.CACHE_MIDDLEWARE_KEY_PREFIX
- Если кеш совместно используется несколькими сайтами с использованием одной и той же установки Django, задайте здесь имя сайта или другую строку, уникальную для этого экземпляра Django, чтобы предотвратить конфликты ключей. Если вам все равно, используйте пустую строку.
FetchFromCacheMiddleware
кэширует ответы GET и HEAD со статусом 200, если это позволяют заголовки запроса и ответа. Ответы на запросы одного и того же URL с разными параметрами запроса считаются уникальными страницами и кэшируются отдельно. Это промежуточное ПО ожидает, что на запрос HEAD ответят те же заголовки, что и на соответствующий запрос GET; в этом случае он может вернуть кэшированный ответ GET на запрос HEAD.
Кроме того, UpdateCacheMiddleware
автоматически устанавливает несколько заголовков в каждом, HttpResponse
которые влияют на последующие кеши :
- Устанавливает
Expires
заголовок на текущую дату / время плюс определенноеCACHE_MIDDLEWARE_SECONDS
. - Устанавливает
Cache-Control
заголовок, чтобы указать максимальный возраст для страницы - опять же, изCACHE_MIDDLEWARE_SECONDS
настройки.
Дополнительную информацию о промежуточном программном обеспечении см. В разделе ПО промежуточного слоя.
Если представление устанавливает собственное время истечения срока действия кеша (т. max-age
Е. В его Cache-Control
заголовке есть раздел ), то страница будет кэшироваться до истечения срока действия, а не CACHE_MIDDLEWARE_SECONDS
. Используя декораторы,
django.views.decorators.cache
вы можете легко установить время истечения срока действия представления (с помощью cache_control()
декоратора) или отключить кеширование для представления (с помощью
never_cache()
декоратора). Дополнительную информацию об этих декораторах см. В разделе «
Использование других заголовков ».
Если USE_I18N
установлено, True
то сгенерированный ключ кеша будет включать имя активного языка - см. Также
Как Django обнаруживает языковые предпочтения ). Это позволяет легко кэшировать многоязычные сайты без необходимости создавать ключ кеширования самостоятельно.
Ключи кеширования также включают текущий часовой пояс, если для USE_TZ
него установлено значение True
.
Кеш для каждого просмотра ¶
-
django.views.decorators.cache.
cache_page
() ¶
Более детальный способ использования фреймворка кэширования - это кэширование вывода отдельных представлений. django.views.decorators.cache
определяет cache_page
декоратор, который автоматически кэширует ответ представления для вас:
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...
cache_page
принимает единственный аргумент: тайм-аут кеша в секундах. В приведенном выше примере результат my_view()
просмотра будет кэшироваться на 15 минут. (Обратите внимание, что мы написали это для удобства чтения. Будет оцениваться как - то есть 15 минут, умноженные на 60 секунд в минуту.)60 * 15
60 * 15
900
Тайм-аут кеширования, установленный с помощью, cache_page
имеет приоритет над max-age
директивой из Cache-Control
заголовка.
Кэш для каждого просмотра, как и для каждого сайта, отключен от URL-адреса. Если несколько URL-адресов указывают на одно и то же представление, каждый URL-адрес будет кэшироваться отдельно. Продолжая my_view
пример, если ваш URLconf выглядит так:
urlpatterns = [
path('foo/<int:code>/', my_view),
]
затем запросы к /foo/1/
и /foo/23/
будут кэшироваться отдельно, как и следовало ожидать. Но как только конкретный URL (например, /foo/23/
) был запрошен, последующие запросы к этому URL будут использовать кеш.
cache_page
также может принимать необязательный аргумент ключевого слова cache
, который указывает декоратору использовать определенный кеш (из ваших
CACHES
настроек) при кэшировании результатов просмотра. По умолчанию
default
будет использоваться кеш, но вы можете указать любой кеш, который хотите:
@cache_page(60 * 15, cache="special_cache")
def my_view(request):
...
Вы также можете переопределить префикс кеша для каждого просмотра. cache_page
принимает необязательный аргумент ключевого слова key_prefix
, который работает так же, как CACHE_MIDDLEWARE_KEY_PREFIX
настройка для промежуточного программного обеспечения. Его можно использовать так:
@cache_page(60 * 15, key_prefix="site1")
def my_view(request):
...
key_prefix
И cache
аргументы могут быть указаны вместе.
key_prefix
Аргумент и KEY_PREFIX
указанные при CACHES
будут слиты.
Кроме того, cache_page
автоматически устанавливает Cache-Control
и
Expires
заголовки в ответе, которые влияют на последующие кеши .
В более старых версиях max-age
директива из Cache-Control
заголовка имела приоритет над таймаутом кеширования, установленным с помощью cache_page
.
Указание кеша для каждого просмотра в URLconf ¶
Примеры в предыдущем разделе жестко закодировали тот факт, что представление кэшируется, потому что cache_page
изменяет my_view
функцию на месте. Этот подход связывает ваше представление с системой кеширования, что не идеально по нескольким причинам. Например, вы можете захотеть повторно использовать функции представления на другом сайте без кеширования или вы можете захотеть распространить представления среди людей, которые могут захотеть использовать их без кеширования. Решением этих проблем является указание кеша для каждого представления в URLconf, а не рядом с самими функциями представления.
Вы можете сделать cache_page
это, заключив функцию представления в оболочку при обращении к ней в URLconf. Вот старый URLconf из более раннего:
urlpatterns = [
path('foo/<int:code>/', my_view),
]
Вот то же самое, но в my_view
упаковке cache_page
:
from django.views.decorators.cache import cache_page
urlpatterns = [
path('foo/<int:code>/', cache_page(60 * 15)(my_view)),
]
Кеширование фрагментов шаблона ¶
Если вам нужен еще больший контроль, вы также можете кэшировать фрагменты шаблона с помощью cache
тега шаблона. Чтобы предоставить вашему шаблону доступ к этому тегу, поместите его
в верхней части шаблона.{% load cache %}
Шаблонный тег кэширует содержимое блока для заданного промежутка времени. Требуется как минимум два аргумента: тайм-аут кеша в секундах и имя для предоставления фрагмента кеша. Если время ожидания истекло, фрагмент кешируется навсегда . Имя будет принято как есть, не используйте переменную. Например:{% cache %}
None
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
Иногда вам может потребоваться кэшировать несколько копий фрагмента в зависимости от некоторых динамических данных, которые появляются внутри фрагмента. Например, вам может понадобиться отдельная кешированная копия боковой панели, использованной в предыдущем примере, для каждого пользователя вашего сайта. Сделайте это, передав один или несколько дополнительных аргументов, которые могут быть переменными с фильтрами или без них, в тег шаблона, чтобы однозначно идентифицировать фрагмент кеша:{% cache %}
{% load cache %}
{% cache 500 sidebar request.user.username %}
.. sidebar for logged in user ..
{% endcache %}
Если USE_I18N
установлено значение True
кэш промежуточного программного обеспечения для каждого сайта, будет
учитываться активный язык . Для cache
тега шаблона вы можете использовать одну из переменных, относящихся к
переводу, доступных в шаблонах, чтобы добиться того же результата:
{% load i18n %}
{% load cache %}
{% get_current_language as LANGUAGE_CODE %}
{% cache 600 welcome LANGUAGE_CODE %}
{% translate "Welcome to example.com" %}
{% endcache %}
Тайм-аут кеширования может быть переменной шаблона, если переменная шаблона разрешается в целочисленное значение. Например, если для переменной шаблона
my_timeout
задано значение 600
, следующие два примера эквивалентны:
{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}
Эта функция полезна, чтобы избежать повторения в шаблонах. Вы можете установить время ожидания в переменной в одном месте и повторно использовать это значение.
По умолчанию тег кеша будет пытаться использовать кеш с именем «template_fragments». Если такого кеша не существует, он вернется к использованию кеша по умолчанию. Вы можете выбрать альтернативный бэкэнд кеширования для использования с using
аргументом ключевого слова, который должен быть последним аргументом тега.
{% cache 300 local-thing ... using="localcache" %}
Указание ненастроенного имени кэша считается ошибкой.
-
django.core.cache.utils.
make_template_fragment_key
( Не fragment_name , vary_on = None ) ¶
Если вы хотите получить ключ кеша, используемый для кэшированного фрагмента, вы можете использовать
make_template_fragment_key
. fragment_name
то же самое, что и второй аргумент cache
тега шаблона; vary_on
список всех дополнительных аргументов, переданных тегу. Эта функция может быть полезна для аннулирования или перезаписи кэшированного элемента, например:
>>> from django.core.cache import cache
>>> from django.core.cache.utils import make_template_fragment_key
# cache key for {% cache 500 sidebar username %}
>>> key = make_template_fragment_key('sidebar', [username])
>>> cache.delete(key) # invalidates cached template fragment
True
API низкоуровневого кеширования ¶
Иногда кеширование всей отображаемой страницы не приносит вам особой выгоды и, по сути, является неудобным излишеством.
Возможно, например, на вашем сайте есть представление, результаты которого зависят от нескольких дорогостоящих запросов, результаты которых меняются с разной периодичностью. В этом случае было бы не идеально использовать полностраничное кэширование, которое предлагают стратегии кеширования для каждого сайта или для каждого просмотра, потому что вы не захотите кэшировать весь результат (поскольку некоторые данные часто меняются), но вы все равно захотите кэшировать результаты, которые редко меняются.
Для подобных случаев Django предоставляет низкоуровневый API кеширования. Вы можете использовать этот API для хранения объектов в кэше с любым уровнем детализации, который вам нравится. Вы можете кэшировать любой объект Python, который можно безопасно обработать: строки, словари, списки объектов модели и так далее. (Наиболее распространенные объекты Python можно мариновать; дополнительную информацию о травлении см. В документации Python.)
Доступ к кешу ¶
-
django.core.cache.
caches
¶ Вы можете получить доступ к кэши , настроенных в
CACHES
настройки через Dict-подобный объект:django.core.cache.caches
. Повторные запросы одного и того же псевдонима в одном потоке вернут один и тот же объект.>>> from django.core.cache import caches >>> cache1 = caches['myalias'] >>> cache2 = caches['myalias'] >>> cache1 is cache2 True
Если названный ключ не существует,
InvalidCacheBackendError
будет поднят.Для обеспечения безопасности потоков для каждого потока будет возвращен отдельный экземпляр серверной части кеша.
-
django.core.cache.
cache
¶ В качестве ярлыка кеш по умолчанию доступен как
django.core.cache.cache
:>>> from django.core.cache import cache
Этот объект эквивалентен
caches['default']
.
Основное использование ¶
Базовый интерфейс:
-
cache.
set
( ключ , значение , тайм-аут = DEFAULT_TIMEOUT , версия = None ) ¶ >>> cache.set('my_key', 'hello, world!', 30)
-
cache.
get
( ключ , по умолчанию = Нет , версия = Нет ) ¶ >>> cache.get('my_key') 'hello, world!'
key
должен быть str
и value
может быть любым выбираемым объектом Python.
timeout
Аргумент является необязательным и по умолчанию к timeout
аргументу соответствующего внутреннего интерфейса в CACHES
установке (описано выше). Это количество секунд, в течение которых значение должно храниться в кеше. Передача
None
for timeout
приведет к кешированию значения навсегда. A timeout
of 0
не кэширует значение.
Если объект не существует в кеше, cache.get()
возвращает None
:
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key')
None
Если вам нужно определить, существует ли объект в кеше, и вы сохранили буквальное значение None
, используйте объект-дозорный по умолчанию:
>>> sentinel = object()
>>> cache.get('my_key', sentinel) is sentinel
False
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key', sentinel) is sentinel
True
MemcachedCache
Из-за python-memcached
ограничения невозможно различить сохраненное None
значение и промах в кэше, обозначенный возвращаемым значением None
в устаревшем MemcachedCache
бэкэнде.
cache.get()
может привести default
аргумент. Это указывает, какое значение вернуть, если объект не существует в кеше:
>>> cache.get('my_key', 'has expired')
'has expired'
-
cache.
add
( ключ , значение , тайм-аут = DEFAULT_TIMEOUT , версия = None ) ¶
Чтобы добавить ключ, только если он еще не существует, используйте add()
метод. Он принимает те же параметры, что и set()
, но он не будет пытаться обновить кеш, если указанный ключ уже присутствует:
>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'
Если вам нужно знать, add()
сохранено ли значение в кеше, вы можете проверить возвращаемое значение. В противном случае он вернется, True
если значение было сохранено
False
.
-
cache.
get_or_set
( ключ , по умолчанию , тайм-аут = DEFAULT_TIMEOUT , версия = None ) ¶
Если вы хотите получить значение ключа или установить значение, если ключа нет в кеше, есть get_or_set()
метод. Он принимает те же параметры, что и, get()
но по умолчанию устанавливается как новое значение кеша для этого ключа, а не возвращается:
>>> cache.get('my_new_key') # returns None
>>> cache.get_or_set('my_new_key', 'my new value', 100)
'my new value'
Вы также можете передать любой вызываемый объект в качестве значения по умолчанию :
>>> import datetime
>>> cache.get_or_set('some-timestamp-key', datetime.datetime.now)
datetime.datetime(2014, 12, 11, 0, 15, 49, 457920)
-
cache.
get_many
( ключи , версия = Нет ) ¶
Также есть get_many()
интерфейс, который попадает в кеш только один раз.
get_many()
возвращает словарь со всеми запрошенными вами ключами, которые действительно существуют в кеше (и срок их действия не истек):
>>> cache.set('a', 1)
>>> cache.set('b', 2)
>>> cache.set('c', 3)
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
-
cache.
set_many
( диктат , тайм-аут ) ¶
Чтобы установить несколько значений более эффективно, используйте set_many()
для передачи словаря пар ключ-значение:
>>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
Типа cache.set()
, set_many()
принимает необязательный timeout
параметр.
На поддерживаемых серверных ВМ (memcached) set_many()
возвращает список ключей, которые не удалось вставить.
-
cache.
delete
( ключ , версия = Нет ) ¶
Вы можете удалить ключи явно с помощью, delete()
чтобы очистить кеш для определенного объекта:
>>> cache.delete('a')
True
delete()
возвращается, True
если ключ был успешно удален, в False
противном случае.
Добавлено логическое возвращаемое значение.
-
cache.
delete_many
( ключи , версия = Нет ) ¶
Если вы хотите очистить сразу несколько ключей, delete_many()
можете взять список очищаемых ключей:
>>> cache.delete_many(['a', 'b', 'c'])
-
cache.
clear
() ¶
Наконец, если вы хотите удалить все ключи в кеше, используйте
cache.clear()
. Будьте осторожны с этим; clear()
удалит все
из кэша, а не только ключи установлены приложения.
>>> cache.clear()
-
cache.
touch
( ключ , тайм-аут = DEFAULT_TIMEOUT , версия = None ) ¶
cache.touch()
устанавливает новый срок действия ключа. Например, чтобы обновить ключ, срок действия которого истекает через 10 секунд:
>>> cache.touch('a', 10)
True
Как и другие методы, timeout
аргумент является необязательным и по умолчанию соответствует
TIMEOUT
параметру соответствующего серверного интерфейса в CACHES
настройке.
touch()
возвращается, True
если клавиша была нажата успешно, в False
противном случае.
-
cache.
incr
( ключ , дельта = 1 , версия = нет ) ¶
-
cache.
decr
( ключ , дельта = 1 , версия = нет ) ¶
Вы также можете увеличивать или уменьшать уже существующий ключ с помощью
методов incr()
или decr()
соответственно. По умолчанию существующее значение кэша будет увеличиваться или уменьшаться на 1. Другие значения увеличения / уменьшения могут быть указаны путем предоставления аргумента для вызова увеличения / уменьшения. Ошибка ValueError будет вызвана, если вы попытаетесь увеличить или уменьшить несуществующий ключ кеша .:
>>> cache.set('num', 1)
>>> cache.incr('num')
2
>>> cache.incr('num', 10)
12
>>> cache.decr('num')
11
>>> cache.decr('num', 5)
6
Примечание
incr()
decr()
Не гарантируется, что / методы будут атомарными. На тех бэкэндах, которые поддерживают атомарное увеличение / уменьшение (в первую очередь, бэкэнд memcached), операции увеличения и уменьшения будут атомарными. Однако, если бэкэнд изначально не предоставляет операцию увеличения / уменьшения, она будет реализована с использованием двухэтапного извлечения / обновления.
-
cache.
close
() ¶
Вы можете закрыть соединение с вашим кешем с помощью, close()
если это реализовано серверной частью кеша.
>>> cache.close()
Примечание
Для кешей, которые не реализуют close
методы, это не работает.
Префикс ключа кеширования ¶
Если вы разделяете экземпляр кеша между серверами или между производственной средой и средой разработки, данные, кэшированные одним сервером, могут использоваться другим сервером. Если формат кэшированных данных на разных серверах различается, это может привести к очень трудным для диагностики проблемам.
Чтобы предотвратить это, Django предоставляет возможность добавлять префиксы ко всем ключам кеша, используемым сервером. Когда конкретный ключ кеша сохраняется или извлекается, Django автоматически префикс ключа кеша со значением
KEY_PREFIX
настройки кеша.
Убедившись, что каждый экземпляр Django имеет разные
KEY_PREFIX
, вы можете гарантировать, что не будет конфликтов в значениях кеша.
Управление версиями кеша ¶
Когда вы изменяете работающий код, который использует кэшированные значения, вам может потребоваться очистить все существующие кэшированные значения. Самый простой способ сделать это - очистить весь кеш, но это может привести к потере значений кеша, которые остаются действительными и полезными.
Django предоставляет лучший способ нацеливать отдельные значения кеша. Инфраструктура кеширования Django имеет общесистемный идентификатор версии, указанный в VERSION
параметрах кеширования. Значение этого параметра автоматически комбинируется с префиксом кэша и предоставленным пользователем ключом кэша для получения окончательного ключа кэша.
По умолчанию любой ключевой запрос будет автоматически включать версию ключа кэша сайта по умолчанию. Однако все примитивные функции кеширования включают version
аргумент, поэтому вы можете указать конкретную версию ключа кеша для установки или получения. Например:
>>> # Set version 2 of a cache key
>>> cache.set('my_key', 'hello world!', version=2)
>>> # Get the default version (assuming version=1)
>>> cache.get('my_key')
None
>>> # Get version 2 of the same key
>>> cache.get('my_key', version=2)
'hello world!'
Версия конкретного ключа может увеличиваться и уменьшаться , используя incr_version()
и decr_version()
методы. Это позволяет перенести определенные ключи на новую версию, не затрагивая другие ключи. Продолжая наш предыдущий пример:
>>> # Increment the version of 'my_key'
>>> cache.incr_version('my_key')
>>> # The default version still isn't available
>>> cache.get('my_key')
None
# Version 2 isn't available, either
>>> cache.get('my_key', version=2)
None
>>> # But version 3 *is* available
>>> cache.get('my_key', version=3)
'hello world!'
Преобразование ключа кеширования ¶
Как описано в предыдущих двух разделах, ключ кеша, предоставленный пользователем, не используется дословно - он комбинируется с префиксом кеша и версией ключа для получения окончательного ключа кеша. По умолчанию три части соединяются двоеточиями для получения финальной строки:
def make_key(key, key_prefix, version):
return '%s:%s:%s' % (key_prefix, version, key)
Если вы хотите объединить части по-разному или применить другую обработку к финальному ключу (например, взяв хеш-дайджест ключевых частей), вы можете предоставить настраиваемую ключевую функцию.
Параметр KEY_FUNCTION
кеширования указывает путь с точками к функции, соответствующей прототипу,
make_key()
описанному выше. Если предоставляется, эта настраиваемая функция клавиш будет использоваться вместо функции комбинирования клавиш по умолчанию.
Предупреждения о ключах кеширования ¶
Memcached, наиболее часто используемый бэкэнд производственного кеша, не позволяет использовать ключи кеша длиной более 250 символов или содержащие пробелы или управляющие символы, и использование таких ключей вызовет исключение. Чтобы поощрять переносимый кеш-код кодом и минимизировать неприятные сюрпризы, другие встроенные серверные части кеша выдают предупреждение ( django.core.cache.backends.base.CacheKeyWarning
), если используется ключ, который может вызвать ошибку в memcached.
Если вы используете производственный бэкэнд, который может принимать более широкий диапазон ключей (пользовательский бэкэнд или один из встроенных бэкэндов без memcached), и хотите использовать этот более широкий диапазон без предупреждений, вы можете отключить звук CacheKeyWarning
с помощью этого кода в management
модуль одного из ваших
INSTALLED_APPS
:
import warnings
from django.core.cache import CacheKeyWarning
warnings.simplefilter("ignore", CacheKeyWarning)
Если вы хотите вместо этого предоставить настраиваемую логику проверки ключей для одного из встроенных серверных модулей, вы можете создать его подкласс, переопределить только validate_key
метод и следовать инструкциям по использованию настраиваемого внутреннего кеша . Например, чтобы сделать это для locmem
бэкэнда, поместите этот код в модуль:
from django.core.cache.backends.locmem import LocMemCache
class CustomLocMemCache(LocMemCache):
def validate_key(self, key):
"""Custom validation, raising exceptions or warnings as needed."""
...
… И используйте пунктирный путь Python к этому классу в
BACKEND
части ваших CACHES
настроек.
Кеши нисходящего потока ¶
До сих пор этот документ был посвящен кэшированию ваших собственных данных. Но для веб-разработки актуален и другой тип кэширования: кэширование, выполняемое «нисходящими» кэшами. Это системы, которые кэшируют страницы для пользователей еще до того, как запрос достигнет вашего веб-сайта.
Вот несколько примеров нижестоящих кешей:
- Ваш интернет-провайдер может кэшировать определенные страницы, поэтому, если вы запросили страницу с https://example.com/ , ваш интернет-провайдер отправит вам страницу без прямого доступа к example.com. Сопровождающие example.com ничего не знают об этом кешировании; Интернет-провайдер находится между example.com и вашим веб-браузером, прозрачно обрабатывая все кэширование.
- Ваш сайт Django может находиться за прокси-кешем , таким как Squid Web Proxy Cache ( http://www.squid-cache.org/ ), который кэширует страницы для повышения производительности. В этом случае каждый запрос сначала будет обрабатываться прокси, и он будет передан вашему приложению только в случае необходимости.
- Ваш веб-браузер тоже кэширует страницы. Если веб-страница отправляет соответствующие заголовки, ваш браузер будет использовать локальную кэшированную копию для последующих запросов к этой странице, даже не обращаясь к веб-странице снова, чтобы увидеть, изменилась ли она.
Нисходящее кэширование - хороший прирост эффективности, но в нем есть опасность: содержимое многих веб-страниц различается в зависимости от аутентификации и множества других переменных, а системы кеширования, которые вслепую сохраняют страницы, основанные исключительно на URL-адресах, могут предоставлять неверные или конфиденциальные данные для последующего использования. посетители этих страниц.
Например, если вы используете веб-систему электронной почты, то содержимое страницы «Входящие» зависит от того, какой пользователь вошел в систему. Если интернет-провайдер слепо кэшировал ваш сайт, то первый пользователь, вошедший через этого интернет-провайдера, будет иметь своего пользователя -конкретная страница почтового ящика кэшируется для последующих посетителей сайта. Это не круто.
К счастью, HTTP предоставляет решение этой проблемы. Существует ряд заголовков HTTP, чтобы дать указание нижестоящим кэшам различать их содержимое в зависимости от назначенных переменных и сообщить механизмам кэширования не кэшировать определенные страницы. Мы рассмотрим некоторые из этих заголовков в следующих разделах.
Использование Vary
заголовков ¶
В Vary
определяет заголовок , который заголовки запроса механизм кэширования следует учитывать при создании своего ключа кэша. Например, если содержимое веб-страницы зависит от языковых предпочтений пользователя, говорят, что страница «зависит от языка».
По умолчанию, система кэширования Django создает свои ключи кэша , используя запрошенную полностью квалифицированное URL - например,
"https://www.example.com/stories/2005/?order_by=author"
. Это означает, что каждый запрос к этому URL-адресу будет использовать одну и ту же кешированную версию, независимо от различий между пользовательскими агентами, таких как файлы cookie или языковые предпочтения. Однако, если эта страница создает разный контент на основе некоторой разницы в заголовках запросов, таких как файл cookie, язык или пользовательский агент, вам необходимо использовать Vary
заголовок, чтобы сообщить механизмам кеширования, что вывод страницы зависит от этих вещи.
Для этого в Django используйте удобный
django.views.decorators.vary.vary_on_headers()
декоратор вида, например:
from django.views.decorators.vary import vary_on_headers
@vary_on_headers('User-Agent')
def my_view(request):
...
В этом случае механизм кэширования (такой как собственное промежуточное ПО кеширования Django) будет кэшировать отдельную версию страницы для каждого уникального пользовательского агента.
Преимущество использования vary_on_headers
декоратора вместо ручной установки Vary
заголовка (с использованием чего-то вроде ) заключается в том, что декоратор добавляет к заголовку (который может уже существовать), а не настраивать его с нуля и потенциально отменять все, что уже было там.response.headers['Vary'] =
'user-agent'
Vary
Вы можете передать несколько заголовков vary_on_headers()
:
@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
...
Это указывает нисходящим кэшам различаться для обоих , что означает, что каждая комбинация пользовательского агента и cookie будет иметь собственное значение кеша. Например, запрос с пользовательским агентом Mozilla
и значением cookie foo=bar
будет считаться отличным от запроса с пользовательским агентом Mozilla
и значением cookie
foo=ham
.
Поскольку изменение файлов cookie является обычным явлением, существует
django.views.decorators.vary.vary_on_cookie()
декоратор. Эти два представления эквивалентны:
@vary_on_cookie
def my_view(request):
...
@vary_on_headers('Cookie')
def my_view(request):
...
Заголовки, в которые вы переходите vary_on_headers
, не чувствительны к регистру;
"User-Agent"
то же самое, что и "user-agent"
.
Вы также можете django.utils.cache.patch_vary_headers()
напрямую использовать вспомогательную функцию . Эта функция устанавливает или дополняет . Например:Vary header
from django.shortcuts import render
from django.utils.cache import patch_vary_headers
def my_view(request):
...
response = render(request, 'template_name', context)
patch_vary_headers(response, ['Cookie'])
return response
patch_vary_headers
принимает HttpResponse
экземпляр в качестве первого аргумента и список / кортеж имен заголовков без учета регистра в качестве второго аргумента.
Подробнее о заголовках Vary см. официальный Vary spec .
Управление кешем: использование других заголовков ¶
Другие проблемы с кешированием - это конфиденциальность данных и вопрос о том, где данные должны храниться в каскаде кешей.
Пользователь обычно сталкивается с двумя типами кешей: их собственный кеш браузера (частный кеш) и кеш поставщика (общедоступный кеш). Публичный кеш используется несколькими пользователями и контролируется кем-то другим. Это создает проблемы с конфиденциальными данными - например, вы не хотите, чтобы номер вашего банковского счета хранился в общедоступном кэше. Таким образом, веб-приложениям нужен способ сообщать кешам, какие данные являются частными, а какие общедоступными.
Решение состоит в том, чтобы указать, что кеш страницы должен быть «частным». Для этого в Django используйте cache_control()
декоратор представления. Пример:
from django.views.decorators.cache import cache_control
@cache_control(private=True)
def my_view(request):
...
Этот декоратор позаботится об отправке соответствующего HTTP-заголовка за кулисами.
Обратите внимание, что параметры управления кешем «частный» и «общедоступный» являются взаимоисключающими. Декоратор гарантирует, что директива «public» будет удалена, если необходимо установить «private» (и наоборот). Примером использования двух директив может быть сайт блога, который предлагает как частные, так и общедоступные записи. Общедоступные записи могут храниться в любом общем кэше. В следующем коде используется
patch_cache_control()
ручной способ изменения заголовка элемента управления кешем (он вызывается изнутри
cache_control()
декоратором):
from django.views.decorators.cache import patch_cache_control
from django.views.decorators.vary import vary_on_cookie
@vary_on_cookie
def list_blog_entries_view(request):
if request.user.is_anonymous:
response = render_only_public_entries()
patch_cache_control(response, public=True)
else:
response = render_private_and_public_entries(request.user)
patch_cache_control(response, private=True)
return response
Вы можете управлять кэшем нисходящего потока и другими способами (см. RFC 7234 для получения подробной информации о кешировании HTTP). Например, даже если вы не используете платформу кеширования на стороне сервера Django, вы все равно можете указать клиентам кэшировать представление на определенное время с помощью директива max-age :
from django.views.decorators.cache import cache_control
@cache_control(max_age=3600)
def my_view(request):
...
(Если вы делаете использовать промежуточное программное обеспечение кэширования, он уже устанавливает max-age
с значением CACHE_MIDDLEWARE_SECONDS
параметра. В этом случае заказ max_age
от
cache_control()
декоратора будет иметь приоритет, а значения заголовка будут объединены правильно.)
Любая допустимая Cache-Control
директива ответа действительна в cache_control()
. Вот еще несколько примеров:
no_transform=True
must_revalidate=True
stale_while_revalidate=num_seconds
no_cache=True
Полный список известных директив можно найти в реестре IANA (обратите внимание, что не все из них применимы к ответам).
Если вы хотите использовать заголовки для полного отключения кеширования,
never_cache()
это декоратор представления, который добавляет заголовки, чтобы гарантировать, что ответ не будет кэшироваться браузерами или другими кешами. Пример:
from django.views.decorators.cache import never_cache
@never_cache
def myview(request):
...
Порядок MIDDLEWARE
¶
Если вы используете промежуточное ПО для кэширования, важно поместить каждую половину в нужное место в MIDDLEWARE
настройках. Это связано с тем, что промежуточное программное обеспечение кеширования должно знать, какие заголовки можно использовать для изменения хранилища кеша. Промежуточное ПО всегда добавляет что-то в Vary
заголовок ответа, когда может.
UpdateCacheMiddleware
выполняется на этапе ответа, где промежуточное ПО запускается в обратном порядке, поэтому элемент в верхней части списка запускается последним на этапе ответа. Таким образом, вам нужно убедиться, что он UpdateCacheMiddleware
появляется перед любым другим промежуточным программным обеспечением, которое может что-то добавить в Vary
заголовок. Следующие промежуточные модули делают это:
SessionMiddleware
добавляетCookie
GZipMiddleware
добавляетAccept-Encoding
LocaleMiddleware
добавляетAccept-Language
FetchFromCacheMiddleware
, с другой стороны, выполняется на этапе запроса, где промежуточное ПО применяется в порядке очереди, поэтому элемент в верхней части списка запускается первым на этапе запроса. FetchFromCacheMiddleware
Также необходимо запустить после других обновлений ПО промежуточного в Vary
заголовок, так
FetchFromCacheMiddleware
должно быть после любого элемента , который делает это.