O framework de cache do Django¶
Uma dilema essencial dos sites dinâmicos vem a ser o próprio fato de serem dinâmicos. Cada vez que um usuário requisita uma página, o servidor web faz todo o tipo de cálculos – consultas a bancos de dados, renderização de templates e lógica de negócio – para criar a página que o seu visitante vê. Isso tem um custo de processamento muito maior que apenas a leitura de arquivos estáticos no disco.
Para a maior parte dos aplicativos Web, esse overhead não é um problema. A maior parte das aplicações Web não são o washingtonpost.com oouslashdot.org; são simplesmente sites pequenos a médio com tráfico equivalente. Mas para aplicações de porte mério para grante, é essencial eliminar toda a sobrecarga possível.
É onde entra o cache.
Fazer o cache de algo é gravar o resultado de um cálculo custoso para que você não tenha de executar o cálculo da próxima vez. Aqui está um pseudocódigo explicando como isso funcionaria para uma página Web gerada dinamicamente:
tente encontrar uma página no cache para tal URL
se a página estiver no cache:
retorne a página do cache
se não:
gere a página
guarde a página gerada no cache (para a próxima vez)
retorne a página gerada
O Django vem com um sistema de cache robusto que permite que você guarde as páginas dinâmicas para que elas não tenham de ser calculadas a cada requisição. Por conveniência, Django oferece diferentes níveis de granularidade de cache: Você pode fazer o cache da saída de views específicas, você pode fazer o cache somente das partes que são difíceis de produzir, ou pode fazer o cache do site inteiro.
O Django também trabalha com caches do tipo “upstream”, como o Squid (http://www.squid-cache.org/) e cache baseado em navegador. Esses são tipos de cache que você não controla diretamente mas para os quais fornece dicas (via cabeçalhos HTTP) sobre quais partes do seu site devem ser mantidas em cache, e como.
Configurando o cache¶
O sistema de cache requer uma pequena configuração. Você deve informar ao Django onde os seus dados em cache estarão – se em um banco de dados, no sistema de arquivos ou diretamente na memória. Essa é uma decisão importante que afeta a performance do seu cache; sim, algums tipos de cache são mais rápidos que outros.
Sua preferência de cache vai na configuração CACHE_BACKEND
no seu arquivo de
configurações. Aqui vai uma explicação de todos os valores disponíveis para
CACHE_BACKEND
.
Memcached¶
De longe, o mais rápido e mais eficiente tipo de cache disponível no Django, Memcached é um framework de cache inteiramente baseado em memória originalmente desenvolvido para lidar com as altas cargas no LiveJournal.com e subsequentemente tornada open-sourced pela Danga Interactive. É usado por sites como o Facebook ou Wikipedia para reduzir o acesso a banco de dados e aumentar a performance do site drasticamente.
O Memcached está disponível de graça em http://danga.com/memcached/ . Ele executa como um daemon, para o qual é alocada uma quantidade específica de RAM. Tudo o que ele faz é fornecer uma interface rápida para adição, busca e remoção de dados arbitrários do cache. Todos os dados são gravados diretamente na memória, então não existe sobrecarga de banco de dados ou uso do filesystem.
Após a instalação do Memcached, você precisa instalar as bibliotecas do Memcached para Python, que não estão empacotadas com o Django. Duas versões estão disponíveis. Selecione e instale um dos seguintes módulos:
- A opção mais rápida é um módulo chamado
cmemcache
, disponível em http://gijsbert.org/cmemcache/ . - Se você não pode instalar o
cmemcache
, você pode instalar opython-memcached
, disponível em ftp://ftp.tummy.com/pub/python-memcached/ . Se essa URL não é mais válida, apenas vá ao site do Memcached (http://www.danga.com/memcached/) e obtenha as bibliotecas Python da seção “Client APIs”.
cmemcache
é nova no 1.0. Anteriormente, somente o
python-memcached
era suportado.Para usar o Memcached com Django, configure o CACHE_BACKEND
para
memcached://ip:port/
, onde ip
é o endereço IP dp daemon do Memcached
e o port
é a porta onde o Memcached está rodando.
Nesse exemplo, o Memcached está rodando em localhost (127.0.0.1) na porta 11211:
CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
Uma característica excelente do Memcached é sua habilidade de compartilhar o cache
em diversos servidores. Isso significa que você pode executar daemons do Memcached em diversas máquinas,e
o programa irá tratar o grupo de máquinas como um único cache, sem a necessidade de duplicar
os valores do cache em cada máquina.Para aproveitar essa funcionalidade, inclua todos os endereços
de servidores em CACHE_BACKEND
, separados por ponto e vírgula.
Nesse exemplo, o cache é compartilhado por instâncias do Memcached rodando nos endereços IP 172.19.26.240 (porta 11211) e 172.19.26.242 (port 11212), e 172.19.26.244 (porta 11213):
CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11212;172.19.26.244:11213/'
O cache baseado em memória tem uma desvantagem: Como os dados em cache estão na memória, serão perdidos se o seu servidor travar. Claramente, a memória não foi feita para armazenamento permanente de dados, então não confie no cache em memória como sua única fonte de armazenamento de dados. Na verdade, nenhum dos backends de cache do Django deve ser usado para armazenamento permanente – devem ser usados como soluções para cache, e não armazenamento – mas reafirmamos isso aqui porque o cache em memória é realmente temporário.
Cache em banco de dados¶
Para usar uma tabela do banco de dados como o seu backend de cache, primeiro crie uma tabela de cache em seu banco de dados com o seguinte comando:
python manage.py createcachetable [cache_table_name]
...onde [cache_table_name]
é o nome da tabela no banco de dados a ser criada.
(Esse nome pode ser qualquer um, desde que seja um nome válido de tabela e esse nome
ainda não esteja sendo usado no seu banco de dados.) Esse comando cria uma única tabela
no seu banco de dados, no formato apropriado que o sistema de cache em banco de dados
do Django espera encontrar.
Uma vez que você tenha configurado a tabela do banco de dados, configure o seu CACHE_BACKEND
para "db://tablename"
, onde tablename
é o nome da tabela.
Nesse exemplo, o nome da tabela de cache é my_cache_table
:
CACHE_BACKEND = 'db://my_cache_table'
O cache em banco de dados usará o mesmo banco de dados especificado em seu arquivo de configuração. Você não pode usar um banco de dados diferente para sua tabela de cache.
O cache em banco de dados funciona melhor se você tem um servidor de banco de dados rápido e bem indexado.
Cache em sistema de arquivos¶
Para gravar os items em cache no sistema de arquivos, use o tipo de cache "file://"
para CACHE_BACKEND
. Por exemplo, para salvar os dados do cache em /var/tmp/django_cache
,
use essa configuração:
CACHE_BACKEND = 'file:///var/tmp/django_cache'
Perceba que existem três barras próximas ao início do exemplo.
As primeiras são para o file://
, e a terceira é o primeiro caractere do
caminho do diretório, /var/tmp/django_cache
. Se você está no Windows, coloque
a letra do drive após o file://
, dessa forma:
file://c:/foo/bar
O caminho do diretório deve ser absoluto – isso é, ele deve iniciar na raiz do seu sistema de arquivos. Não faz diferença se você põe a barra no final da configuração.
Assegure-se que o diretório apontado por essa configuração exista e tenha permissões
de leitura e escrita pelo usuário do sistema que executa o seu servidor web.
Continuando o exemplo acima, se o seu servidor web roda como o usuário apache
,
tenha certeza que o diretório /var/tmp/django_cache
exista e tenha permissão de
leitura e escrita pelo usuário apache
.
Cada valor de cache será gravado em um arquivo separado cujos conteúdos
são os dados servidos pelo cache em um formato serializado (“pickled”),
usando o módulo pickle
do Python. Cada nome de arquivo é a chave do cache,
escapado para uso seguro em sistema de arquivos.
Cache em memória local¶
Se você quer as vantagens de velocidade de executar um cache em memória mas não
pode executar o Memcached, considere o backend de cache em memória local. Esse
cache é multi-processo e thread-safe. Para usá-lo, configure o CACHE_BACKEND
para
"locmem:///"
. Por exemplo:
CACHE_BACKEND = 'locmem:///'
Note que cada processo irá ter sua própria instância privada de cache, o que significa que nenhum cache entre processos é possível. Isso obviamente significa que o cache em memória local não é muito eficiente em termos de memória, então provavelmente não é uma boa escolha para um ambiente de produção. É bom para desenvolvimento.
Cache falso (para desenvolvimento)¶
Finalmente, o Django vem com um cache “falso” que não faz cache realmente – ele apenas implementa a interface de cache sem fazer nada realmente.
Isso é útil se você tem um site em produção que usa cacheamento pesado em
vários lugares, mas em desenvolvimento ou no ambiente de testes você não quer
usar cache e não quer mudar o código para lidar com o última caso em especial. Para
ativar o cache falso, configure o CACHE_BACKEND
assim:
CACHE_BACKEND = 'dummy:///'
Usando um backend de cache personalizado¶
Apesar do Django suportar diversos sistemas de cache diferentes,
algumas vezes você pode querer usar algum backend de cache personalizado. Para usar um
backend externo de cache com o Django, use um caminho de importação de módulos do Python
como a parte do esquema (a parte que vem antes do dois pontos inicial) da URI do
CACHE_BACKEND
, assim:
CACHE_BACKEND = 'path.to.backend://'
Se você está construindo o seu próprio backend, você pode usar os backends padrão
de cache como implementações de referência. Você irá encontrar o código no diretório
django/core/cache/backends/
dos fontes do Django.
Nota: Você deveria usar os backends de cache incluídos com o Django, anão ser que você tenha uma razão muito boa, como um host que não os suporta. Eles foram bem testados e são fáceis de usar.
Argumentos do CACHE_BACKEND¶
Cada tipo de cache pode receber argumentos. Eles são informados em um estilo semelhante a
query-string na configuração CACHE_BACKEND
. Os seguintes argumentos são válidos:
timeout
: O timeout padrão, em segundos, a ser usado para o cache. O padrão é 300segundos (5 minutos).
max_entries
: Para os backendslocmem
,filesystem
edatabase
,o número máximo de entradas permitidas no cache antes dos valores antigos serem removidos. O valor padrão desse argumento é 300.
cull_frequency
: A fração de entradas que são limpas do cache quandomax_entries
é atingido. A razão real é1/cull_percentage
, então configure ocull_percentage=2
para limpar metadae das entradas quando o valor demax_entries
for atingido.Um valor de
0
para ocull_frequency
significa que o cache todo será limpo quandomax_entries
for atingido. Isso torna a limpeza muito mais rápida, a custa de mais perdas no cache.
Nesse exemplo, o timeout
está configurado para 60
:
CACHE_BACKEND = "memcached://127.0.0.1:11211/?timeout=60"
Nesse exemplo, o timeout
é 30
e max_entries
é 400
:
CACHE_BACKEND = "locmem:///?timeout=30&max_entries=400"
Argumentos inválidos são silenciosamente ignorados, assim como valores inválidos para argumentos conhecidos.
O cache por site¶
CacheMiddleware
no lugar
das duas partes descritas abaixo).Uma vez que o cache esteja configurado, a forma mais siples de usá-lo é fazer
o cache do seu site inteiro. Você precisa adicionar
'django.middleware.cache.UpdateCacheMiddleware'
e
'django.middleware.cache.FetchFromCacheMiddleware'
as suas configurações de
MIDDLEWARE_CLASSES
, como nesse exemplo:
MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
)
Note
No, isso não é um erro de digitação: o middleware “update” deve aparecer primeiro na list, e o middleware “fetch” por último. Os detalhes são um pouco obscuros, mas veja Ordem das MIDDLEWARE_CLASSES abaixo se você quiser entender os detalhes.
Então, adicione as configurações necessárias ao seu arquivo de configurações do Django:
CACHE_MIDDLEWARE_SECONDS
– O número de segundos em que cada página deve permancecer em cache.CACHE_MIDDLEWARE_KEY_PREFIX
– Se o cache é compartilhado entre diversos sites usando a mesma instalação do Django, configure isso para o nome do site, ou alguma outra string única que identifique esse instância do Django, para previnir colisões de cache. Use uma string vazia se você não se importa.
O middleware de cache faz o cache de cada página que não tenha parâmetros de GET ou
POST. Opcionalmente, se a configuração CACHE_MIDDLEWARE_ANONYMOUS_ONLY
for
True
, somente requisições anônimas (i.e., aquelas feitas por usuários não autenticados)
serão cacheadas. Esse é um modo simples e eficiente para desabilita o cache para quaisquer
páginas específicas de usuários (incluindo a interface de administração do Django). Note que se você
usou o CACHE_MIDDLEWARE_ANONYMOUS_ONLY
, você deve certificar-se de ter ativado o
AuthenticationMiddleware
.
Adicionalmente, o middleware de cache adiciona alguma cabeçalhos em cada
HttpResponse
:
- Configura o cabeçalho
Last-Modified
para a data/hora atuais quando uma nova versão da página (não cacheada) é resiquitada. - Configura o cabeçalho
Expires
para a hora atual mais o valor definido emCACHE_MIDDLEWARE_SECONDS
. - Configura o cabeçalho
Cache-Control
para dar um max age para a página – novamente, da configuraçãoCACHE_MIDDLEWARE_SECONDS
.
Veja Middleware para mais sobre middlewares.
Se uma visão configura o seu próprio tempo de expiração de cache (i.e. tem uma seção max-age
no seu cabeçalho Cache-Control
) então a página será mantida em cache até o tempo de
edxpiração, ao invés de CACHE_MIDDLEWARE_SECONDS
. Usando os decoradores em
django.views.decorators.cache
você pode facilmente configurar o tempo de expiração de
uma visão(usando o decorador cache_control
) ou desabilitar o cache para uma visão (usando
o decorador never_cache
). Veja a seção usando outros cabeçalhos para mais
informações sobre esses decoradores.
O cache por visão¶
Uma forma mais granular de usar o framework de cache é fazer o cache da saída
de visões individuais. django.views.decorators.cache
define um decorador
cache_page
que ierá automaticamente fazer o cache da resposta da visão para você.
É fácil de usar:
from django.views.decorators.cache import cache_page
def my_view(request):
...
my_view = cache_page(slashdot_this, 60 * 15)
Ou, usando a sintaxe de decorador do Python 2.4:
@cache_page(60 * 15)
def slashdot_this(request):
...
cache_page
recebe um único argumento: o timeout do cache, em segundos. No exemplo
acima, o resultado da visão slashdot_this()
será mantido em cache por 15
minutos. (Note que escrevemos isso como 60 * 15
visando legibilidade.
60 * 15
será avaliado para 900
– isso é, 15 minutos multiplicados
por 60 segundos por minuto.)
O cache por visão, como o cache por site, é indexado de acordo com a URL. Se
múltiplas URLs apontam para a mesma visão, cada URL será cacheada separadamente.
Continuando o exemplo my_view
, se o seu URLconf for semelhante a isso:
urlpatterns = ('',
(r'^foo/(\d{1,2})/$', my_view),
)
então requisições para /foo/1/
e /foo/23/
serão cacheadas separadamente, conforme
você pode esperar. Mas uma vez que uma URL em particular (ex: /foo/23/
) tenha sido
requisitada, acessos subsequentes a essa URL usarão o cache.
Especificando o cache por visão no URLconf¶
Os exemplos na seção anterior tem hard-coded o fato que a visão é
cacheada, porque cache_page
altera a função my_view
in place. Esse
método acopla suas visões ao sistema de cache, o que não é o ideal por diversas
razões. Por exemplo, você pode querer reusar as funções de visão em outro site,
sem cache, ou você pode querer distribuir suas visões para pessoas que podem
querer usá-las sem cache. A solução para esses problemas é
especificar o cache por visão no URLconf ao invés de configurá-lo nas
próprias funções.
Fazer isso é fácil: simplemente embrulhe a função de visão com cache_page
quando você
se referir a ela no URLconf. Aqui está a nossa já conhecida URLconf:
urlpatterns = ('',
(r'^foo/(\d{1,2})/$', my_view),
)
Aqui está a mesma coisa, com my_view
embrulhada em cache_page
:
from django.views.decorators.cache import cache_page
urlpatterns = ('',
(r'^foo/(\d{1,2})/$', cache_page(my_view, 60 * 15)),
)
Se você usar esse método, não se esqueça de importar cache_page
dentro de seu
URLconf.
Cache de fragmento de template¶
Se você quer ainda mais controle, você pode também fazer cache de fragmentos de
template usando a tag de template cache
. Para permitir que seu template use essa tag, coloque
{% load cache %}
perto do topo do seu template.
A tag de template {% cache %}
faz o cache do conteúdo do bloco por um certo período
de tempo. Recebe ao menos dois argumentos: o timeout do cache, em segundos,
e o nome que será dado ao cache de fragmento. Por exemplo:
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
Algumas vezes você pode querer adicionar ao cache diversas cópias de um fragmento dependendo de
algum dado dinâmico que apareça dentro do fragmento. Por exemplo, você pode querer uma
cópia separada da barra lateral usada no exemplo anterior para cada usuário
do seu site. Faça isso passando argumentos adicionais a template tag {% cache %}
que unicamente identifiquem esse cache de fragmento:
{% load cache %}
{% cache 500 sidebar request.user.username %}
.. sidebar for logged in user ..
{% endcache %}
É perfeitamente válido especificar mais de um argumento para identificar o fragmento.
Simplesmente passe tantos argumentos quanto você precisar para {% cache %}
.
O timeout de cache pode ser uma variável de template, desde que a variável de template seja um
valor inteiro. Por exemplo, se a variável de template
my_timeout
está configurada para o valor 600
, então os dois exemplos a seguir são
equivalentes:
{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}
Essa característica é útil para evitar repetição em seus templates. Você pode configurar o timeout em uma variável, em um lugar, e apenas reutilizar esse valor.
A API de baixo nível do cache¶
Algumas vezes, o cache de uma página completa renderizada não é de grande valor, é pode ser até inconveniente.
Talvez, por exemplo, o seu site inclua uma visão cujos resultados dependam de algumas consultas intensivas ao banco de dados, cujos resultados mudem em diferentes intervalos. Nesse caso, não seria ideal usar o cache completo de página que as estratégias de cache por site ou por visão oferecem, porque você não quer fazer o cache do resultado completo (já que alguns dados mudam frequentemente), mas você ainda gostaria de manter em cache os resultados que mudam com menos frequência.
Para casos como esse, o Django expõe uma API simples de baixo nível de cache. Você pode usar essa API para guardar objetos no cache com quaisquer níveis de granularidade que você quiser. Você pode cacher quaisquer objetos Python que podem ser serializados com segurança: strings, dicionários, listas ou objetos do modelo, e por aí vai. (A maioria dos objetos comum do Python podem ser serializados através de pickle; veja a documentação do Python para mais informações sobre o pickling.)
Por exemplo, você pode descobrir que basta fazer o cache do resultado de uma consulta intensiva ao banco de dados. Em casos assim, você pode usar a API de baixo nível de cache para guardar os objetos em qualquer nível de granularidade que você quiser.
O módulo de cache, django.core.cache
tem um objeto cache``que é
criado automaticamente a partir da configuração ``CACHE_BACKEND
:
>>> from django.core.cache import cache
A interface básica é set(key, value, timeout_seconds)
e get(key)
:
>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
'hello, world!'
O argumento timeout_seconds
é opcional e o seu padrão é o mesmo do argumento timeout
na configuração CACHE_BACKEND
(explicada acima).
Se o objeto não existe no cache, cache.get()
retorna None
:
# Espere 30 segundos para 'my_key' expirar...
>>> cache.get('my_key')
None
We advise against storing the literal value None
in the cache, because you
won’t be able to distinguish between your stored None
value and a cache
miss signified by a return value of None
.
cache.get()
pode ter um argumento padrão
. Isso especifica qual valor retornar
se o objeto não existe no cache:
>>> cache.get('my_key', 'has expired')
'has expired'
Para adicionar uma cache somente se ela ainda não existir, use o método add()
.
Ele recebe os mesmos parâmetros que set()
, mas não irá tentar atualizar o cache se a
chave especificada já estiver presente:
>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'
Se você precisa saber se add()
salvou um valor no cache, você pode
verificar o valor retornado. O retorno será True
se o valor foi gravado,
e False
se nada for gravado.
Existe também uma interface chamada get_many()
que acessa o cache apenas uma vez.
get_many()
retorna um dicionário com todas as chaves que você pediu
que estavam no cache e não estavem expiradas):
>>> cache.set('a', 1)
>>> cache.set('b', 2)
>>> cache.set('c', 3)
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
Finalmente, você pode remover chaves diferentes com delete()
. Essa é uma forma
fácil de limpar o cache para um objeto em particular:
>>> cache.delete('a')
Please, see the release notes
Você pode também incrementar ou decrementar uma chave que já existe usando os métodos
incr()
ou decr()
, respectivamente. Por padrão, o valor existente em cache
será incrementado ou decrementado em 1. Outros valores para incremento/decremento
podem ser especificados fornecendo um argumento para a chamada increment/decrement. Um
ValueError será lançado se você tentar incrementar ou decrementar uma
chave de cache não existente.:
>>> cache.set('num', 1)
>>> cache.incr('num')
2
>>> cache.incr('num', 10)
12
>>> cache.decr('num')
11
>>> cache.decr('num', 5)
6
Note
incr()
/decr()
não tem garantia de serem atômicos. Nos
backends que suportam incremento/decremento atômico (mais notavelmente, o
backend memcached), operações de incremento e decremento serão atômicas.
Porém, se o backend não provê nativamente uma operação de incremento/decremento,
ela será implementada usando uma operação obter/atualizar de dois passos.
Caches Upstream¶
Até agora, esse documento concentrou-se no cache de seus próprios dados. Mas outro tipo de cache é relevante para o desenvolvimento Web: o cache executado por cache “upstream”. Esses são sistemas que fazem o cache de páginas para usuários antes mesmo da requisição chegar ao seu Web site.
Aqui estão algumns exemplos de cache upstream:
- Seu ISP pode fazer o cache de certas páginas, então se você requisitou uma página de http://example.com/, seu ISP poderia enviar a página sem ter de acessar o example.com diretamente. Os mantenedores de example.com não tem conhecimento desse cache; O ISP fica entre example.com e o seu navegador Web, lidando com todo o cache de forma transparente.
- Seu Web site Django pode ficar atrás de um proxy cache como o proxy Squid (http://www.squid-cache.org/), que faz cache de páginas para performance. Nesse caso, cada requisição seria tratada primeiramente pelo proxy e seria passada para a sua aplicação somente se necessário.
- Seu navegador web também faz cache. Se uma página envia os cabeçalhos apropriados, seu navegador irá usar a cópia local (em cache) para requisições subsequentes para essa página, sem nem mesmo contatar a página Web novamente paraver se o conteúdo mudou.
Caches upstream são um potencializador de eficiência interessante, mas há um perigo nisso: muitos conteúdos de páginas web diferem baseados na autenticação e em diversas outras variáveis, e sistemas de cache que servem páginas baseadas em URL cegamente poderiam expor dados incorretos ou sensíveis para visitantes subsequentes a essas páginas.
Por exemplo, digamos que você opere um serviço de webmail, e o conteúdo da página “inbox” obviamente depende do usuário que está logado. Se um ISP cegamente cacheia seu site, então o primeiro usuário que logou através desse ISP poderia ter seu conteúdo da caixa de entrada exposto aos próximos visitantes do site. Isso não é legal.
Felizmente, o HTTP fornece uma solução para esse problema: um número de cabeçalhos HTTP existem para instruir os cache upstream a diferir seus conteúdos de cache dependendo de certas variáveis definidas, e como dizer aos mecanismos de cache como não cachear páginas particulares. Veremos alguns desses cabeçalhos na seção a seguir.
Usando cabeçalhos Vary¶
O cabeçalho Vary
define quais cabeçalhos de requisição
um mecanismo de cache deve levar em conta ao construir sua chave de cache.
Por exemplo, se o conteúdo do site de uma págfina web depende da preferência de
idioma do usuário, dizemos que a página “varia no idioma.”
Por padrão, o sistema de cache do DJango cria suas chaves de acesso usando o caminho
da requisição (ex: "/stories/2005/jun/23/bank_robbed/"
). Isso significa que cada
requisição a essa URL irá usar a mesma versão do cache, não importando as diferentes
de agente de usuário, como cookies ou preferência de idioma. Porém, se essa página
produz conteúdos diferentes baseados em alguma diferença de cabeçalhos de requisição – como
um cookie, ou um idioma, ou um agente de usuário – você irá precisar usar o cabeçalho Vary
para dizer aos mecanismos de cache que a saída da página depende dessas coisas.
Para fazer isso no Django, use o decorador de visão de conveniência vary_on_headers
,
assim:
from django.views.decorators.vary import vary_on_headers
# Sintaxe do Python 2.3.
def my_view(request):
# ...
my_view = vary_on_headers(my_view, 'User-Agent')
# Sintaxe de decorador do Python 2.4 ou superior.
@vary_on_headers('User-Agent')
def my_view(request):
# ...
Nesse caso, um mecanismo de cache (como o próprio middleware de cache do Django) irá cachear uma versão diferente da página para cada agente de usuário único.
A vantagem de usar o decorador vary_on_headers
ao invés de configurar o cabeçalho
Vary
manualmente (usando algo como
response['Vary'] = 'user-agent'
) é que o decorador adiciona algo ao cabeçalho Vary
(que pode já existir), ao invés de sobrescrevê-lo
e potencialmente apagar algo que já estava lá.
Você pode passar diversos cabeçalhos para vary_on_headers()
:
@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
# ...
Isso diz aos cache upstream para variar em ambos os cabeçalhos, o que significa que cada combinação de
agente de usuário e cookie irá ter seu próprio valor de cache. Por exemplo, uma requisição com
o agente de usuário Mozilla
e o valor de cookie foo=bar
será considerado
diferente de uma requisição com um agente de usuário Mozilla
e o valor de cookie
foo=ham
.
Como a variação baseada em cookie é um caso comum, existe um decorador vary_on_cookie
.
Essas duas views são equivalentes:
@vary_on_cookie
def my_view(request):
# ...
@vary_on_headers('Cookie')
def my_view(request):
# ...
Os cabeçalhos que você passa para vary_on_headers
não são
sensíveis a caso. "User-Agent"
é a mesma coisa que "user-agent"
.
Você pode também usar uma função auxiliar, django.utils.cache.patch_vary_headers
,
diretamente. Essa função configura ou adiciona ao cabeçalho Vary
. Por exemplo:
from django.utils.cache import patch_vary_headers
def my_view(request):
# ...
response = render_to_response('template_name', context)
patch_vary_headers(response, ['Cookie'])
return response
patch_vary_headers
recebe uma instância de HttpResponse
como seu primeiro argumento
e uma lista/tupla de nomes de cabeçalho não sensíveis a caso como seu segundo argumento.
Para mais sobre os cabeçalhos Vary, veja a especificação oficial do Vary.
Controlando o cache: Usando outros cabeçalhos¶
Outros problemas com o cache são a privacidade de dados e a questão de onde os dados devem ser armazenados em uma cascada de caches.
Um usuário normalmente encontra dois tipos de cache: o do seu próprio navegador (um cache privado) e o do seu provedor (um cache público). Um cache público é usado por muitos usuários e controlado por mais alguém. Isso coloca problemas com dados sensíveis–você não quer, por exemplo, que o número da sua conta bancária seja gravada em um cache público. Assim, aplicações web precisam de uma forma de dizer aos caches quais dados são privados e quais dados são públicos.
A solução é indicar que o cache de página seja “particular”. Para fazer isso no
Django, use o decorador de visão cache_control
. Exemplo:
from django.views.decorators.cache import cache_control
@cache_control(private=True)
def my_view(request):
# ...
Esse decorador cuida de enviar o cabeçalho HTTP apropriado nos bastidores.
Existem algumas outras formas de controlar os parâmetros de cache. Por exemplo, o HTTP permite que aplicações façam o seguinte:
- Define o tempo máximo que uma página deva permanecer em cache.
- Especifica se um cache deve sempre verificar por novas versões da página, exibindo o conteúdo em cache apenas quando não houve mudanças. (Alguns caches podem entregar conteúdo em cache mesmo se a página no servidor mudou – simplesmente porque a cópia do cache ainda não expirou.)
No Django, use o decorador de visão cache_control
para especificar esses parâmetros
de cache. Nesse exemplo, cache_control
diz ao cache para revalidar o
cache em cada acesso e para guardar versões em cache em no máximo por 3600 segundos:
from django.views.decorators.cache import cache_control
@cache_control(must_revalidate=True, max_age=3600)
def my_view(request):
# ...
Qualquer diretiva HTTP Cache-Control
é válida em cache_control()
.
Aqui está uma lista completa:
public=True
private=True
no_cache=True
no_transform=True
must_revalidate=True
proxy_revalidate=True
max_age=num_seconds
s_maxage=num_seconds
Para explicação das diretivas HTTP de Cache-Control, veja a Especificação de Cache-Control.
(Note que o middleware de cache já configura o tempo máximo (max-age) do cache com
o valor da configuração CACHE_MIDDLEWARE_SETTINGS
. Se você usa um max_age
personalizado no decorador cache_control
, o decorador terá precedência sobre a configuração,
e os valores do cabeçalho serão mesclados corretamente.)
Se você quer usar cabeçalhos para desabilitar todo o cache,
django.views.decorators.cache.never_cache
é um decorador de visão que adiciona
cabeçalhos que garantem que a resposta não será posta em cache por navegadores ou outros caches.
Exemplo:
from django.views.decorators.cache import never_cache
@never_cache
def myview(request):
# ...
Outras otimizações¶
O Django vem com alguns outros middlewares que podem ajudar a otimizar a performance do seu aplicativo:
django.middleware.http.ConditionalGetMiddleware
adiciona suporte para GET condicional em navegadores modernos, baseado nos cabeçalhosETag
eLast-Modified
.django.middleware.gzip.GZipMiddleware
comprime conteúdo para todos os navegadores modernos reduzindo o uso de banda e o tempo de transferência.
Ordem das MIDDLEWARE_CLASSES¶
Se você está usando o middleware de cache, é importador colocar cada parte dentro do lugar correto nas
configurações de MIDDLEWARE_CLASSES
. Isso porque o cabeçalho de cache
precisa saber por quais cabeçalhos ele irá variar o armazenamento de cache.
Middleware sempre adiciona algo ao cabeçalho Vary
quando ele pode.
UpdateCacheMiddleware
executa durante a fase de resposta, onde os middlewares são
executados em ordem reversa, então um item no topo da lista sempre executa por último durante
a fase de resposta. Assim, você precisa garantir que UpdateCacheMiddleware
apareça antes de quaisquer outros middlewares que possam adicionar algo ao cabeçalho Vary
.
Os seguintes módulos de middleware fazem isso so:
SessionMiddleware
adicionaCookie
GZipMiddleware
adicionaAccept-Encoding
LocaleMiddleware
adicionaAccept-Language
FetchFromCacheMiddleware
, por outro lado, executa durante a fase de requisição,
onde os middlewares são aplicados do primeiro ao último, então um item no topo da lista
execute primeiro durante a fase de requisição. O FetchFromCacheMiddleware
também
precisa edxecutar depois de alguns outros middlewares atualizarem o cabeçalho Vary
, assim
FetchFromCacheMiddleware
deve ser configurado após qualquer item que faça isso.