[Rafael Carício] Melhoria de performance projetos Django usando Gunicorn, Nginx e Memcached

Estou estudando formas para a melhoria de performance de aplicações na web. Atualmente eu trabalho com Python/Django no desenvolvimento de aplicações web. Pesquisando na internet sobre como melhorar o tempo de respostas das requisições ao máximo encontrei vários artigos explicando diversas formas de fazer isso. Muitos desses artigos me chamaram a atenção, pois faz exatamente o que eu imaginava. O próprio Nginx se comunica com o memcached e verifica se existe, aquela determinada página que o usuário está requisitando, no cache e apenas se não tiver, é que a requisição é repassada para o stack do python/django.

 

Depois de visualizar esse funcionamento eu resolvi implementar alguns testes em uma simples aplicação rodando localmente para verificar este funcionamento e os ganhos em performance que isso pode proporcionar. Fiz uma compilação das ideias apresentadas pelos artigos que eu li sobre o assunto.

Instalando o software necessário

Começei instalando o memcached e colocando ele para rodar com 512mb de mémoria.

Pronto, depois disso podemos verificar se o memcached está rodando corretamente.

Agora o memcached está instalado e funcionando. Vamos instalar o nginx, é bem simples.

E também temos que instalar o suporte ao memcached no Python e instalar também o Gunicorn para rodar nossa aplicação.

Hora de verificar se o nginx está rodando.

Podemos ver que está acessando a url http://localhost/ e vendo a mensagem “Wellcome to nginx!”.

Configurando para a aplicação rodar usando o gunicorn

Agora vamos configurar o nginx para acessar nossa aplicação que vamos colocar rodando sobre o gunicorn. Primeiro vamos configurar o nginx para enviar repassar as requisições.

No arquivo de configuração, eu coloquei:

 

Temos que deletar/desabilitar as configurações padrão do nginx, pois nem vamos usar para este caso. Eu preferi deletar o arquivo.

 

rafaelcaricio@ubuntu:~$ sudo rm /etc/nginx/sites-enabled/default 

 

 

Agora criamos um link para as configurações no diretório de sites-enabled do gunicorn.

rafaelcaricio@ubuntu:~$ sudo ln /etc/nginx/sites-available/easyproject.conf /etc/nginx/sites-enabled/easyproject.conf 

E reiniciamos o nginx para pegar as novas configurações.

 

rafaelcaricio@ubuntu:~$ sudo /etc/init.d/nginx reload

Reloading nginx configuration: the configuration file /etc/nginx/nginx.conf syntax is ok

configuration file /etc/nginx/nginx.conf test is successful

nginx.

Pronto, agora se acessarmos http://localhost vamos ver um erro 502 do nginx, isso acontece porque nossa aplicação não está rodando ainda. Vamos colocar nossa aplicação django para rodar agora. Para isso, precisamos criar a configuração do gunicorn pra rodar o projeto. Assim eu crio o arquivo gunicorn.conf.py dentro do meu projeto django.

Essas são as configurações básicas que fazem meu projeto executar corretamente. Agora podemos mandar executar o gunicorn com as configurações escolhidas.

rafaelcaricio@ubuntu:~/development/easyproject$ gunicorn_django -c gunicorn.conf.py settings.py

Pronto! Agora já podemos acessar nossa aplicação através do nginx que repassa as requisições para o gunicorn. Isso já deixa a nossa aplicação bem rápida. Porém esse é o básico que podemos fazer pra deixar tudo rodando. A minha ideia aqui é ir um pouco mais além e deixar as coisas funcionando mais rápido ainda. E para isso vou fazer um esquema de cache na aplicação django criando um middleware para salvar os resultados das requisições no memcached. E deixar o nginx perguntar ao memcached sobre a existência do resultado da requisição antes de repassar a requisição para o django.

Para isso acontecer eu modifiquei as configurações no nginx.

 

 

E também criei um novo middleware que foi adicionado ao meu projeto no django. E adicionei algumas opções ao meu settings.py. 

 

 

No settings.py eu adicionei as seguintes configurações:

 

 

Assim, todas as páginas que o django serve que sejam GET serão adicionadas ao memcached e o nginx vai pegar de lá o seu conteúdo. Assim as respostas ficarão bem mais rápidas.

Conclusão

Esta configuração é incrivelmente mais rápida, pois as requisições não vão direto para o django. O django só vai processar requisições POST, telas de erro e no caso de uma nova página ser acessada pela primeira vez. A ideia agora é pensar mais a frente, em como invalidar esse cache para que as informações mostradas aos usuários estejam sempre atualizadas. Essa tarefa não é muito complexa, apenas será uma coisa a mais que vai ter que ser feita quando houver modificações no banco de dados. Porém merece uma atenção e cuidado maior, para não ter grande impacto no processo de desenvolvimento. Vou analisar várias implementações e técnicas de invalidação de cache. Assim, no próximo post eu falarei mais sobre isso e demostrarei qual foi a solução que eu encontrei para isso.

 

Referências

http://kovyrin.net/2007/08/05/using-nginx-ssi-and-memcache-to-make-your-web-applications-faster/

http://amix.dk/blog/post/19414

http://jimmyg.org/blog/2009/ssi-memcached-nginx.html

http://www.willmcgugan.com/blog/tech/2009/3/1/fast-caching-with-django-and-nginx/

http://tabbedthinking.posterous.com/nginx-memcached-uwsgi-django

http://soyrex.com/articles/django-nginx-memcached.html