Redis
 sql >> Base de Dados >  >> NoSQL >> Redis

Cache no Django com Redis


O desempenho do aplicativo é vital para o sucesso do seu produto. Em um ambiente em que os usuários esperam tempos de resposta do site inferiores a um segundo, as consequências de um aplicativo lento podem ser medidas em dólares e centavos. Mesmo que você não esteja vendendo nada, carregamentos rápidos de página melhoram a experiência de visitar seu site.

Tudo o que acontece no servidor entre o momento em que ele recebe uma solicitação até o momento em que ele retorna uma resposta aumenta o tempo que leva para carregar uma página. Como regra geral, quanto mais processamento você puder eliminar no servidor, mais rápido seu aplicativo será executado. Armazenar dados em cache após serem processados ​​e servi-los a partir do cache na próxima vez que forem solicitados é uma maneira de aliviar o estresse no servidor. Neste tutorial, exploraremos alguns dos fatores que atrapalham seu aplicativo e demonstraremos como implementar o armazenamento em cache com o Redis para neutralizar seus efeitos.

Bônus grátis: Clique aqui para obter acesso a um Guia de Recursos de Aprendizagem Django (PDF) gratuito que mostra dicas e truques, bem como armadilhas comuns a serem evitadas ao construir aplicativos web Python + Django.

O que é Redis?


Redis é um armazenamento de estrutura de dados na memória que pode ser usado como um mecanismo de cache. Como mantém os dados na RAM, o Redis pode entregá-los muito rapidamente. Redis não é o único produto que podemos usar para armazenamento em cache. O Memcached é outro sistema de cache na memória popular, mas muitas pessoas concordam que o Redis é superior ao Memcached na maioria das circunstâncias. Pessoalmente, gostamos de como é fácil configurar e usar o Redis para outros fins, como o Redis Queue.


Primeiros passos


Criamos um aplicativo de exemplo para apresentar o conceito de cache. Nosso aplicativo usa:
  • Django (v1.9.8)
  • Barra de ferramentas de depuração do Django (v1.4)
  • django-redis (v4.4.3)
  • Redis (v3.2.0)

Instale o aplicativo


Antes de clonar o repositório, instale o virtualenvwrapper, caso ainda não o tenha. Esta é uma ferramenta que permite instalar as dependências específicas do Python que seu projeto precisa, permitindo que você direcione as versões e bibliotecas exigidas pelo seu aplicativo isoladamente.

Em seguida, altere os diretórios para onde você mantém os projetos e clone o repositório do aplicativo de exemplo. Uma vez feito, altere os diretórios para o repositório clonado e crie um novo ambiente virtual para o aplicativo de exemplo usando o mkvirtualenv comando:
$ mkvirtualenv django-redis
(django-redis)$

OBSERVAÇÃO: Criando um ambiente virtual com mkvirtualenv também o ativa.

Instale todas as dependências necessárias do Python com pip e, em seguida, faça o checkout da seguinte tag:
(django-redis)$ git checkout tags/1

Conclua a configuração do aplicativo de exemplo criando o banco de dados e preenchendo-o com dados de exemplo. Certifique-se de criar um superusuário também, para que você possa fazer login no site de administração. Siga os exemplos de código abaixo e tente executar o aplicativo para verificar se está funcionando corretamente. Visite a página de administração no navegador para confirmar se os dados foram carregados corretamente.
(django-redis)$ python manage.py makemigrations cookbook
(django-redis)$ python manage.py migrate
(django-redis)$ python manage.py createsuperuser
(django-redis)$ python manage.py loaddata cookbook/fixtures/cookbook.json
(django-redis)$ python manage.py runserver

Depois de executar o aplicativo Django, vá para a instalação do Redis.


Instalar o Redis


Baixe e instale o Redis usando as instruções fornecidas na documentação. Como alternativa, você pode instalar o Redis usando um gerenciador de pacotes como apt-get ou preparação caseira dependendo do seu sistema operacional.

Execute o servidor Redis em uma nova janela de terminal.
$ redis-server

Em seguida, inicie a interface de linha de comando (CLI) do Redis em uma janela de terminal diferente e teste se ela se conecta ao servidor Redis. Usaremos a CLI do Redis para inspecionar as chaves que adicionamos ao cache.
$ redis-cli ping
PONG

O Redis fornece uma API com vários comandos que um desenvolvedor pode usar para atuar no armazenamento de dados. Django usa django-redis para executar comandos no Redis.

Observando nosso aplicativo de exemplo em um editor de texto, podemos ver a configuração do Redis em settings.py Arquivo. Definimos um cache padrão com o CACHES configuração, usando um django-redis embutido cache como nosso back-end. O Redis é executado na porta 6379 por padrão e apontamos para esse local em nossa configuração. Uma última coisa a mencionar é que django-redis acrescenta nomes de chave com um prefixo e uma versão para ajudar a distinguir chaves semelhantes. Neste caso, definimos o prefixo como “exemplo”.
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient"
        },
        "KEY_PREFIX": "example"
    }
}

OBSERVAÇÃO :Embora tenhamos configurado o backend de cache, nenhuma das funções de visualização implementou o cache.



Desempenho do aplicativo


Como mencionamos no início deste tutorial, tudo o que o servidor faz para processar uma solicitação diminui o tempo de carregamento do aplicativo. A sobrecarga de processamento da lógica de negócios em execução e dos modelos de renderização pode ser significativa. A latência da rede afeta o tempo que leva para consultar um banco de dados. Esses fatores entram em jogo toda vez que um cliente envia uma solicitação HTTP ao servidor. Quando os usuários iniciam muitas solicitações por segundo, os efeitos no desempenho tornam-se perceptíveis à medida que o servidor trabalha para processar todas elas.

Quando implementamos o cache, deixamos o servidor processar uma solicitação uma vez e depois a armazenamos em nosso cache. À medida que as solicitações para a mesma URL são recebidas pelo nosso aplicativo, o servidor extrai os resultados do cache em vez de processá-los novamente a cada vez. Normalmente, definimos um tempo de vida dos resultados armazenados em cache, para que os dados possam ser atualizados periodicamente, o que é uma etapa importante a ser implementada para evitar a veiculação de dados obsoletos.

Você deve considerar armazenar em cache o resultado de uma solicitação quando os seguintes casos forem verdadeiros:
  • renderizar a página envolve muitas consultas de banco de dados e/ou lógica de negócios,
  • a página é visitada com frequência por seus usuários,
  • os dados são os mesmos para todos os usuários,
  • e os dados não mudam com frequência.

Comece medindo o desempenho


Comece testando a velocidade de cada página em seu aplicativo comparando a rapidez com que seu aplicativo retorna uma resposta após receber uma solicitação.

Para conseguir isso, estaremos explodindo cada página com uma explosão de solicitações usando loadtest, um gerador de carga HTTP e, em seguida, prestando muita atenção à taxa de solicitação. Acesse o link acima para instalar. Uma vez instalado, teste os resultados em relação ao /cookbook/ Caminho do URL:
$ loadtest -n 100 -k  http://localhost:8000/cookbook/

Observe que estamos processando cerca de 16 solicitações por segundo:
Requests per second: 16

Quando observamos o que o código está fazendo, podemos tomar decisões sobre como fazer alterações para melhorar o desempenho. O aplicativo faz 3 chamadas de rede para um banco de dados com cada solicitação para /cookbook/ , e leva tempo para cada chamada abrir uma conexão e executar uma consulta. Visite o /cookbook/ URL em seu navegador e expanda a guia Django Debug Toolbar para confirmar esse comportamento. Encontre o menu “SQL” e leia o número de consultas:

livro de receitas/services.py
from cookbook.models import Recipe


def get_recipes():
    # Queries 3 tables: cookbook_recipe, cookbook_ingredient,
    # and cookbook_food.
    return list(Recipe.objects.prefetch_related('ingredient_set__food'))

livro de receitas/views.py
from django.shortcuts import render
from cookbook.services import get_recipes


def recipes_view(request):
    return render(request, 'cookbook/recipes.html', {
        'recipes': get_recipes()
    })

O aplicativo também renderiza um modelo com alguma lógica potencialmente cara.
<html>
<head>
  <title>Recipes</title>
</head>
<body>
{% for recipe in recipes %}
  <h1>{{ recipe.name }}</h1>
    <p>{{ recipe.desc }}</p>
  <h2>Ingredients</h2>
  <ul>
    {% for ingredient in recipe.ingredient_set.all %}
    <li>{{ ingredient.desc }}</li>
    {% endfor %}
  </ul>
  <h2>Instructions</h2>
    <p>{{ recipe.instructions }}</p>
{% endfor %}
</body>
</html>


Implementar cache


Imagine o número total de chamadas de rede que nosso aplicativo fará quando os usuários começarem a visitar nosso site. Se 1.000 usuários acessarem a API que recupera as receitas do livro de receitas, nosso aplicativo consultará o banco de dados 3.000 vezes e um novo modelo será renderizado a cada solicitação. Esse número só cresce à medida que nosso aplicativo é dimensionado. Felizmente, essa visualização é uma ótima candidata para armazenamento em cache. As receitas em um livro de receitas raramente mudam, ou nunca. Além disso, como a visualização de livros de receitas é o tema central do aplicativo, a API que recupera as receitas é garantida para ser chamada com frequência.

No exemplo abaixo, modificamos a função view para usar o cache. Quando a função é executada, ela verifica se a chave de visualização está no cache. Se a chave existir, o aplicativo recuperará os dados do cache e os retornará. Caso contrário, o Django consulta o banco de dados e então armazena o resultado no cache com a chave view. A primeira vez que esta função é executada, o Django irá consultar o banco de dados e renderizar o template, e então fará uma chamada de rede para o Redis para armazenar os dados no cache. Cada chamada subsequente para a função ignorará completamente o banco de dados e a lógica de negócios e consultará o cache do Redis.

exemplo/configurações.py
# Cache time to live is 15 minutes.
CACHE_TTL = 60 * 15

livro de receitas/views.py
from django.conf import settings
from django.core.cache.backends.base import DEFAULT_TIMEOUT
from django.shortcuts import render
from django.views.decorators.cache import cache_page
from cookbook.services import get_recipes

CACHE_TTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)


@cache_page(CACHE_TTL)
def recipes_view(request):
    return render(request, 'cookbook/recipes.html', {
        'recipes': get_recipes()
    })

Observe que adicionamos o @cache_page() decorador para a função de visualização, juntamente com um tempo de vida. Visite o /cookbook/ URL novamente e examine a barra de ferramentas de depuração do Django. Vemos que são feitas 3 consultas ao banco de dados e 3 chamadas ao cache para verificar a chave e salvá-la. O Django salva duas chaves (1 chave para o cabeçalho e 1 chave para o conteúdo da página renderizada). Recarregue a página e observe como a atividade da página muda. Na segunda vez, 0 chamadas são feitas para o banco de dados e 2 chamadas são feitas para o cache. A nossa página está agora a ser servida a partir da cache!

Quando reexecutamos nossos testes de desempenho, vemos que nosso aplicativo está carregando mais rápido.
$ loadtest -n 100 -k  http://localhost:8000/cookbook/

O armazenamento em cache melhorou a carga total e agora estamos resolvendo 21 solicitações por segundo, cinco a mais do que nossa linha de base:
Requests per second: 21


Inspecionando o Redis com a CLI


Neste ponto, podemos usar a CLI do Redis para ver o que é armazenado no servidor Redis. Na linha de comando do Redis, insira as keys * comando, que retorna todas as chaves que correspondem a qualquer padrão. Você deverá ver uma chave chamada “example:1:views.decorators.cache.cache_page”. Lembre-se, “example” é nosso prefixo de chave, “1” é a versão e “views.decorators.cache.cache_page” é o nome que o Django dá à chave. Copie o nome da chave e insira-o com o get comando. Você deve ver a string HTML renderizada.
$ redis-cli -n 1
127.0.0.1:6379[1]> keys *
1) "example:1:views.decorators.cache.cache_header"
2) "example:1:views.decorators.cache.cache_page"
127.0.0.1:6379[1]> get "example:1:views.decorators.cache.cache_page"

OBSERVAÇÃO: Execute o flushall comando na CLI do Redis para limpar todas as chaves do armazenamento de dados. Em seguida, você pode executar as etapas deste tutorial novamente sem precisar esperar que o cache expire.



Encerramento


O processamento de solicitações HTTP é caro, e esse custo aumenta à medida que seu aplicativo cresce em popularidade. Em alguns casos, você pode reduzir bastante a quantidade de processamento que seu servidor faz implementando o cache. Este tutorial abordou os fundamentos do armazenamento em cache no Django com Redis, mas apenas deu uma olhada superficial em um tópico complexo.

A implementação do cache em um aplicativo robusto tem muitas armadilhas e armadilhas. Controlar o que é armazenado em cache e por quanto tempo é difícil. A invalidação de cache é uma das coisas difíceis em Ciência da Computação. Garantir que os dados privados possam ser acessados ​​apenas por seus usuários pretendidos é uma questão de segurança e deve ser tratada com muito cuidado durante o armazenamento em cache.

Bônus grátis: Clique aqui para obter acesso a um Guia de Recursos de Aprendizagem Django (PDF) gratuito que mostra dicas e truques, bem como armadilhas comuns a serem evitadas ao construir aplicativos web Python + Django.

Brinque com o código-fonte no aplicativo de exemplo e enquanto você continua a desenvolver com o Django, lembre-se de sempre manter o desempenho em mente.