Posts de ‘Rafael Carício’

[Rafael Carício] Standards in specification of RESTful API’s and why do it?

Thursday, October 18th, 2012

I have been looking for ways and standards to specify RESTful API’s. Since then I have been with a lot of tabs opened in my web browser just to not loose the url’s and interesting things I have found out there. So, I realized that I can maybe help someone else if I put all those url’s I found in a blog post. So, then here I am.

Below is a list of links I have found and some thoughts about each of them.

In my current job I’m writing an micro-framework called Tapioca for help people to create API’s using Tornado. I have decided to create this project because I didn’t found anything similar to use with Tornado. In Django you can use Tastypie or Django-rest Framework. Tornado projects can be made in so far different ways than in Django because in Django you normally follow an common sense to create projects and the framework is used in all levels of the application, since the database access until the web interface. Using Tornado you are completely free to use anything to database access and you not tells the framework which are and how are your models.
The objective of Tapioca is to provide a common behaviour to manage resources as close as possible to the definition of your RESTful API. I’m trying to follow the best practices of creation of RESTful API’s. Other objective is to provide a automatic API specification in WADL and Swagger - so, then you will be able to use the apigee console or generate your own console using Swagger-ui.
I am working right now to figure out a way to generate those metadata to generate the specifications about an API created using Tapioca. I will post soon about it and while I am woking you can track what I am doing in the project github.
I’m not a native english speaker so if you found anything that I wrote is incorrect. Please, tell me and I will correct it.

[Rafael Carício] Finalmente entendi o PyPy

Saturday, February 25th, 2012

Olá pessoal, depois de muito tempo sem postar nada aqui estou voltando a escrever alguma coisa. Espero começar a escrever mais por aqui. Vou tentar.

Como muitos sabem, eu curto muito essa parte de implementação de compiladores e interpretadores e etc. E sempre que posso fico lendo coisas legais sobre este assunto. Recentemente Bernardo Heynemann me enviou um post de um cara que estava implementando sua própria linguagem. O autor (Laurence Tratt) percebeu que precisava melhorar ainda mais a performance dessa linguagem, porém escrever um JIT do zero é uma tarefa muito complicada e trabalhosa. Foi ai que Laurence Tratt se deparou com o PyPy e resolveu (depois de alguns testes e estudos) portar a implementação de sua linguagem para o PyPy e obteve ganhos de performance absurdos. Se quiser mais detalhes, é melhor ler o post. O que eu quero destacar aqui é que durante a leitura do post eu entendi, finalmente, o projeto PyPy.

Afinal o que é o PyPy?

Muitas pessoas tentaram me explicar alguns anos atrás, mas só agora que caiu a ficha. O PyPy tem como objetivo ser uma implementação alternativa mais rápida do interpretador da linguagem Python (versão 2.7.2) o CPython. Só que as pessoas sempre me vendiam o PyPy como a implementação de Python em Python. Eu nunca entendia isso direito e deixava pra lá. Até o belo dia em que eu li aquele post e parei para realmente entender. O PyPy (de forma bem abstrata) é um nome para dos projetos diferentes mas que sevem para solucionar um problema: criar uma implementação mais rápida da linguagem Python. São eles:

  • RPython Toolchain - um projeto para implementação de linguagens dinâmicas que usa como DSL um subset da linguagem Python que é chamado de RPython. Seve para implementar interpretadores para qualquer linguagem de programação usando RPython. Essa implementação é usada para gerar código nativo (muito) otimizado.
  • Uma implementação da linguagem Python - esta implementação é compatível com a versão 2.7.2 do CPython e este interpretador é escrito em RPython.

Devido a o nome PyPy se referir a estes “dois” projetos. Muita gente se confunde, e muito, sobre o que é o PyPy (assim como eu). O que eu achei mais interessante depois deste entendimento é que eu posso usar o RPython para implementar interpretadores para qualquer linguagem que eu quiser e com isso ganhar até um JIT de brinde para esta nova linguagem.

O que estou fazendo agora é estudando melhor o RPython para usar-lo em um projeto de linguagem de programação que quero fazer. Estou bastante motivado a fazer isso e espero postar resultados dessa brincadeira por aqui em breve. Para maiores dúvidas e detalhes sobre o PyPy a documentação pode ser encontrada aqui.

[Rafael Carício] Implementando uma Toy Programming Language

Sunday, July 17th, 2011

Tudo que uma linguagem de programação precisa é de uma estrutura de loop, expressões, listas e funções. Concordam?

[update] - algumas linguagens não tem nenhuma estrutura de loop. Como Haskell que usa pattern matching pra resolver o problema de estruturas de controle e recursão para loops.

Estou implementando uma linguagem sem nome (ainda usando um nome temporário), e estou pensando em implementar o básico que uma linguagem moderna precisa. Estou implementando para aprimorar meus próprios conhecimentos sobre programação e relembrar coisas que aprendi na época da faculdade. Aqui vai um exemplo da sintaxe da linguagem que estou implementando:

Tudo na linguagem é função, expressão, lista e valor. Inclusive o “if”. Que nela é uma função que é definida na propria linguagem:

E usado desta foma:

O “*” informa ao interpretador que estou querendo referenciar a variável “k” no escopo dinâmico (escopo onde a função está sendo executada) e não no escopo local interno da função, onde modificações a esta variável não fariam diferença para o escopo onde a função foi chamada (caso exista uma variável com o mesmo nome).
Na linguagem existe também apenas uma estrutura de loop que é o “while”. E funciona da seguinte forma:

Com o “while” eu posso definir como é minha estrutura de “for”. Assim:

E usar da seguinte forma:

Assim o valor de “a” é [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]. E outra coisa interessante que posso definir é a função “map”, que ficaria da seguinte forma:

Nesta linguagem o comportamento do “map” é o mesmo do “for each” pois uma função SEMPRE retorna um valor nem que ele seja “Null”.
Com o essas funções previamente definidas, podemos implementar uma função de “filter” e ficaria assim:

Os “_” underlines nestas funções servem para não conflitar com nome das variáveis do escopo onde o programador vai usar.

Quem quiser saber mais sobre este projeto pode acompanhar pelo github. Aceito reclamações, xingamentos, elogios e etc.

https://github.com/rafaelcaricio/unamed

[Rafael Carício] Cookie SQL Injection - Explorando uma falha de segurança na prática

Sunday, November 7th, 2010

Quando converso com desenvolvedores Web eu percebo que são poucos que têm conhecimento de programação segura para Web. A algum tempo atrás eu trabalhava com Java e tinha/tenho muita preocupação com a questão de segurança. Cheguei até a tentar lecionar um curso de programação segura para Web em Java na uCon Security Conference, porém devido a falta de interesse o curso acabou não acontecendo. Acredito que conhecer, no mínimo, as formas de ataques mais básicas é fundamental para qualquer desenvolvedor Web. Um bom lugar para começar é conhecendo o projeto o Open Web Application Security Project (OWASP). Lá vocês podem encontrar uma vasta gama de informações sobre segurança na Web. Um sub-projeto interessante do OWASP é o que lista as 10 vulnerabilidades mais encontradas na Web.

Como mencionei no post anterior, o código em C que postei para melhorar a performance no acesso de aplicações Django usando o NGINX e Memcached tem uma falha de segurança que permite ataques de SQL Injection. Inicialmente eu pensei em realizar a correção no código e depois postar no blog. Mas depois me ocorreu que eu poderia mostrar como explorar esta falha. Achei que isso poderia ser bastante interessante para vocês verem como funciona esse tipo de ataque.

Bem, olhando o código escrito em C talvez não seja tão evidente a existência da falha. Pois normalmente os ataques de SQL Injection teem uma forma de entrada do código malicioso e saída da informação recolhida dentro do sistema. Normalmente esse problema é encontrado em aplicações Web onde é verificada a informação retornada em algum campo que é apresentado dentro do código HTML. Mas as falhas e aplicações se comportam totalmente diferentes em sistemas diferentes. A criatividade deve entrar em ação para tentar descobrir como interferir no sistema e obter uma resposta com sucesso. Ao analisar o comportamento da aplicação em funcionamento aparentemente não encontramos nenhuma forma de obter um retorno sobre qual o dado que está armazenado. O que conhecemos é o comportamento do sistema. Quando um visitante acessa a aplicação ele pode obter uma resposta da aplicação Web em Django ou do código em C onde existe a evidência de falha. Então sabemos que nossa atenção deve ser em cima de como fazer a requisição ir para o código vulnerável. Assim, lembrando do post anterior, todas as requisições passam pelo código em C onde é feita uma consulta a base de dados pelos dados existentes na sessão do usuário que no Django (e na maioria, se não, todos os tipos de aplicações web) armazena o Session Key no cookie do browser do usuário. O código realiza uma consulta a base de dados sobre a sessão e verifica o tamanho desses dados, caso seja maior que 60 caracteres (que foi o tamanho máximo identificado por mim para uma sessão de usuário não autenticado) a requisição é repassada para o Django e é entendido que o usuário está autenticado. Caso contrário, o usuário não está autenticado e a requisição é retornada a partir dos dados existentes no Memcached. Analisando esse fluxo, podemos perceber que a chave para o ataque é identificar como saber se a requisição vem do código em C ou do Django. E usar isso para obter uma resposta, já que este SQL executado (e que contém a vulnerabilidade) só é usado para isso. Assim procurei e achei a diferença entre as respostas do Django e do código vulnerável. Quando a resposta vem do Django, existe uma variável que aparece no cabeçalho da resposta a requisição HTTP. Ao descobrir isso, eu já sabia quando uma requisição vinha do Django e quando vinha do código em C e sabia que se o resultado retornado depois da execução do código SQL se fosse maior que 60 ele ia passar pra o Django e caso fosse menor ele mesmo iria responder. Assim, eu agora precisava só descobri como usar isso para obter informações sobre o sistema, no caso, informações importantes armazenadas na base de dados.

Primeiro eu precisava escolher que informação eu iria tentar obter, dentro do contexto teria que ser algo que estivesse no banco de dados da aplicação. Eu sei que toda aplicação Django com autenticação tem uma tabela chamada auth_user onde ficam armazenados os usuários, então escolhi atacar essa tabela e obter informações sobre os usuários da aplicação.

Bem, depois de descobrir onde deveria atacar eu precisei imaginar (usar a criatividade para) pensar em como fazer a resposta da execução do SQL ser maior que 60 caso a condição que eu verificasse fosse verdadeira ou falsa. Assim eu poderia usar a informação que a resposta veio do Django ou não para validar o que eu estaria perguntando ao banco de dados.

OFF-Topic: Resolvi ir tomar um banho e ficar pensando sobre como fazer isso. Minhas idéias fluem quando estou tomando banho e é lá onde eu tenho muitas ideias interessantes. Acho que quase todas que já postei ou vou postar neste blog surgiram quando eu estava tomando banho.

No final de todo o processo de pensar testar a avaliar o funcionamento eu escrevi este código. A ideia dele é fazer uma requisição a aplicação usando cada letra do alfabeto ou caractere válido para o campo que eu queria e fazer com que a resposta vá para o Django sempre que eu achar o caractere certo. Assim eu teria que iterar caractere por caractere para descobrir um-por-um. E foi isso que eu fiz.

Este código varre todas as possibilidades sobre cada caractere e vai validando cada entrada. No final podemos obter o nome de um usuário que é staff do sistema. Este mesmo código, sem muitas modificações pode ser usado para obter muitas outras informações. Ai vale a criatividade de cada um. Isso é apenas um exemplo simples.

A criatividade é um fator fundamental a qualquer pessoa que deseja realizar um ataque. Pois cada ataque requer um cuidado ou ação bem especifica. Cada sistema tem um funcionamento e foi implementado de uma forma totalmente diferente. Por isso que dificilmente um ataque que obteve sucesso sobre um sistema vai obter o mesmo sucesso sobre outro. O importante na questão de segurança é entender o fundamento do ataque e não decorar como foi feito. O fato da forma de atacar cada sistema ser diferente leva a que poucas pessoas realizem ou tenham sucesso ao tentar realizar esse tipo de atividade.

Espero que tenham gostado do post e ainda tenho muitas ideias que não postei aqui. Muita coisa ainda está por vir. A interação de vocês por meio de comentários é muito importante para minha motivação a continuar postando coisas aqui. Por isso, escrevam suas opniões, criticas ou elogios tudo isso é sempre bem-vindo.

[Rafael Carício] Acrescentando métodos ou atributos ao objeto User do Django

Friday, November 5th, 2010

O Django é um framework para desenvolvimento web em python muito bem conceituado. Facilita o desenvolvimento web entre outras coisas que podem ser encontradas no site oficial do framework.
Estou usando ele para desenvolver um projeto relativamente grande, com muitas funcionalidades (atualmente em fase de desenvolvimento). Um dia desses tive que modificar o modelo de dados do sistema para adicionar mais um tipo de usuário. No inicio o sistema tinha apenas um tipo de perfil que era o de estudante, porém tive que adicionar o profile do professor no sistema. Quando um novo usuário vai entrar no sistema ele escolhe se é estudante ou professor, dependendo da escolha o sistema cria um tipo de profile diferente. Ai foi onde tive problema, pois o Django já é preparado para trabalhar com sistemas que utilizam profile, porém sistemas com apenas um profile (um tipo de usuário). Procurei bastante na internet sobre isso e acabei achando uma solução interessante, porém não me lembro mais qual foi o blog gringo que achei sobre isso (vou pesquisar e achando eu atualizo aqui). Adicionei este código no arquivo urls.py :

from django.contrib.auth.models import User

class UserGetProfileExtension:
    def _get_profile(self):
        if not hasattr(self, ‘_UserGetProfileExtension__cached_profile’):
            try:
                if hasattr(self, ’student’):
                    self.__cached_profile = self.student
                elif hasattr(self, ‘professor’):
                    self.__cached_profile = self.professor
            except:
                self.__cached_profile = None
        else:
            self.__cached_profile = self._UserGetProfileExtension__cached_profile
        return self.__cached_profile
    profile = property(_get_profile)
User.__bases__ += ( UserGetProfileExtension, )

Agora para pegar o profile do usuário basta continuar usando:

user.profile

Espero que isso possa ajudar muita gente. Como as referências estão em inglês, este conteúdo em português é interessante. Até logo e depois vou colocando mais soluções interessantes.

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

Friday, November 5th, 2010

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

 

[Rafael Carício] Deployment de Django usando NGINX, Memcached e um pouco de código em C

Friday, November 5th, 2010
Depois do post anterior eu começei a pensar em como eu poderia melhorar ainda mais a performance da nossa aplicação Django. Então tive a ideia de fazer cache das páginas estáticas e servi-las direto do memcached para o usuário. Isso funcionou muito bem, mas existe um problema quando essa mesma ideia é aplicada a uma aplicação onde os usuários realizam login e precisam acessar páginas personalizadas com coisas do tipo “Olá {{ nome_do_usuario }}, bem-vindo!”. E pensei bastante sobre isso e deixei pra resolver este problema depois. Resolvi atacar inicialmente os visitantes externos, que não são usuários do sistema consequentemente que não estão autenticados no sistema. Neste grupo de visitantes se encontram também os engines de pesquisa como Google, Yahoo!, Bing e etc. E também os navegantes de primeira viagem que chegaram no site. Assim, quanto melhor a experiência que estes visitantes tiverem no primeiro acesso ao site maior as chances deles voltares ou se cadastrarem para usar o sistema. A velocidade de carregamento das páginas do site é um dos criterios de qualidade que estes visitantes analisam para decidirem se voltam ou não. Inclusive o Google recentemente anunciou que vai usar/usa o tempo de carregamento como critério no seu algoritmo de PageRank, ou seja, mais um motivo para motivar a desbravar esta ideia.

Para deixar o acesso dos visitantes não autenticados mais rápido não precisamos pensar muito, se você leu o post anterior e fez um teste simples deve ter percebido os ganhos em velocidade que foram extremamente absurdos. Assim, precisamos enviar esses visitantes não autenticados para o memcached diretamente sem que a requisição passe pela stack do Python / Django. Dessa forma desafogamos um pouco (ou muito) nossa aplicação pois ela só vai processar requisições que são realmente interessantes e que precisam de um processamento mais dinâmico. Se pararmos pra pensar, percebemos que os visitantes não precisam acessar as páginas com dados atualizando em tempo real (depende muito da aplicação, mas de um modo geral isso é verdade). Por exemplo, se nossa aplicação tem um forúm os visitantes do site não precisam ver o forúm se atualizando a cada acesso ou a cada segundo que ele visita o site. Já os usuários autenticados, precisam e devem ver o forúm atualizado o mais rápido possível para que eles possam interagir com os outros usuários do site. E essa ideia é aplicada a todas as páginas externas do site pois nem os engines de pesquisa ficam acessando o site a cada milisegundo pra ver o que mudou. Resumindo, os visitantes do site não precisam ver as coisas atualizadas eles provávelmente estão entrando no site pela primeira vez e tudo pra eles é novidade.

Agora que decidimos que precisamos fazer cache de tudo que é acessado por usuários não autenticados ficou fácil, não é? Não, como vamos saber quando o usuário está autenticado ou não? No código Python / Django podemos descobrir isso fácil mas como descobrir isso antes da requisição chegar ao Python? Precisamos validar se o usuário está autenticado ou não o mais rápido possível e enviar a resposta pra ele. O ideal é descobrir isso diretamente no NGINX, mas não tem uma forma de fazer isso. Inicialmente eu pensei em validar a existência do cookie do Django nas requisições e se ele existisse o usuário estaria autenticado. Porém isso não funciona pois o Django cria um cookie de sessão mesmo se o usuário não for autenticado. Sendo assim, o que fazer? Essa foi minha dúvida e decidi estudar o código do Django nessa parte de autenticação e gerenciamento de sessão para saber o que eu preciso fazer pra descobrir se o usuário está autenticado ou não. Depois de fazer isso, resumindo, cheguei a conclusão que não dava pra deserializar o objeto pickler que o Django salva no BD pra descobrir se o usuário está autenticado ou não. Então, analisando o comportamento do cookie do Django eu percebi que dava pra fazer uma validação simples, que não dá 100% de acerto sobre a questão do usuário estar autenticado ou não, mas já é uma boa (do meu ponto de vista). Eu descobri que posso validar o tamanho dos dados da sessão, não dá pra deserializar o objeto da sessão, mas dá pra validar o tamanho da string salva no BD. E é assim que eu faço a validação pra saber se o usuário está autenticado. Se os dados da sessão forem maior que um limite mínimo, quer dizer que o usuário está autenticado. No meu caso isso funciona pra mim porquê eu não salvo nada na sessão quando o usuário não está autenticado (é importante essa informação). Se você salva algo na sessão do usuário em páginas públicas da sua aplicação esse esquema, do jeito que eu faço agora, pode não funcionar pra você.

Resolvido esses problemas, eu tentei resolver o problema de acessar o banco de dados para pegar os dados que estão armazenados na sessão do usuário e validar se ele está autenticado antes da requisição ir para o Django. Essa validação tinha que ser feita no próprio NGINX ou em alguma coisa bastante rápida pra conseguir obter essa resposta. Então procurei várias alternativas. Primeiro eu pensei em usar node.js, mas cai no problema que eu ainda não conheço a tecnologia direito e então isso poderia ser um problema. Tentei procurar outra coisa, um modulo para o NGINX, talvez. Até achei um módulo que dá pra usar código em Lua no NGINX, mas eu também tinha que aprender Lua e não me pareceu necessário nesse momento também. Tentei achar uma outra solução e foi ai que achei um post em um blog mostrando um exemplo de consulta ao memcached usando código em C. Entao me ocorreu que eu poderia modificar esse código para atender as minhas necessidades. E foi isso que eu fiz. Código em C é o mais rápido que eu poderia conseguir (tá, eu sei… mas eu não sei Assembly tão bem assim, ainda).

Para explicar a ideia que eu tive, eu criei alguns diagramas de sequencia pra ficar mais fácil de entender. Abaixo eu mostro a primeira situação. Nesse caso eu mostro o que acontece quando o usuário acessa a aplicação e não está autenticado. Eu chamo o meu código em C de UserRouter, ele valida se o usuário está autenticado e se não estiver ele consulta no memcached a página que o usuário está tentando acessar e envia de volta para o NGINX no caso da página existir no memcached. Caso a página não exista ele envia a requisição para o Django normalmente.

arquitetura

No segundo cenário eu quero mostrar o que acontece quando um usuário autenticado acessa a página ou quando não existe uma página pública ainda em cache.

sem dados

A parte que coloca a página no memcached é opcional, pois se for uma página interna da aplicação ou uma página que apenas usuários autenticados acessam, ele nunca vai pra o memcached.
Então, para suportar esse funcionamento eu reusei o middleware do post anterior.

Adicionei algumas configurações ao settings.py:

E o código em C que faz toda essa mágica acontecer.

PS.: Bem, eu só vou avisando logo que esse código em C tem uma falha de SQL Injection que eu ainda vou corrigir (e atualizo aqui pra vocês).
Pra compilar esse código vocês teem que instalar algumas bibliotecas, mas essa parte é fácil se você usa o ubuntu.

rafaelcaricio@ubuntu:~/development$ sudo apt-get install libmysqlclient-dev libevent-dev libmemcached-dev

E pra compilar o código. A linha de comando é:

rafaelcaricio@ubuntu:~/development$ gcc -o v_auth_and_cache -levent -I/usr/include/mysql  -DBIG_JOINS=1  -fno-strict-aliasing   -DUNIV_LINUX -DUNIV_LINUX verify_auth_and_cache.c -Wl,-Bsymbolic-functions -rdynamic -L/usr/lib/mysql -lmysqlclient -lmemcached

Depois é só executar o servidor UserRouter colocando na porta 8000. Usando essa linha de comando:
rafaelcaricio@ubuntu:~/development$ ./v_auth_and_cache localhost 8000

E fazer algumas alterações na configuração no NGINX. Assim a requisição sempre vai primeiro pra o UserRouter e depois, se for o caso, passa para o Django.


No final eu realizei bastante testes e verifiquei que tudo isso realmente funciona e deixa o acesso a páginas externas extremamente rápido. Certo que eu ainda vou testar muito isso e melhorar esse funcionamento para poder usar no AtéPassar. Mas tenho certeza que isso vai melhorar bastante a velocidade de indexação pelos engines de busca e melhorar também a experiência dos usuários que estão acessando o site pela primeira vez. Eles vão ver tudo funcionando incrivelmente rápido. Espero ter ajudado ou aberto a mente de alguns de vocês para novas possibilidades. Em breve estarei escrevendo mais um post sobre outra ideia ou sobre alguma outra coisa que eu já fiz nas minhas horas vagas. Abraços e até logo.

[Rafael Carício] Profiling de aplicações Django

Friday, November 5th, 2010

Hoje tive que identificar um problema complicado no AtéPassar, uma rede social de estudantes de concursos públicos e OAB onde eu trabalho atualmente como líder técnico da equipe de desenvolvimento. A aplicação estava ficando lenta quando tinha muitos acessos simutâneos e não havia motivos aparentes para isso acontecer. Devido a esse problema tive que procurar uma forma de descobrir o que estava acontendo internamente no código da aplicação. Assim, lembrei que existe uma técnica de analise de programas que ecaixa totalmente com esta situação que é o profiling de software.

Pesquisei bastante na internet sobre como fazer isso em aplicações Django. Python tem um módulo especifico para isso, que é o hotshot. Assim acabei encontrando um projeto bem interessante que já existe uma integração com o django para rodar sobre o hotshot. Então ficou mais fácil de realizar essa tarefa de analisar o funcionamento do projeto. E para a visualização das chamadas eu utilizei o KCachegrind, um projeto OpenSource que permite visualizar os dados gerados por softwares de profiling.

Screenshot do KCachegrind

O gráfico gerado pelo KCachegrind mostra as funções do sistema e os caminhos de chamadas com o custo de cada chamada. Assim podemos visualizar e identificar qual o ponto especifico que está deixando o sistema lento.

Depois de utilizar essas ferramentas consegui identificar o problema que estava deixando o AtéPassar lento e agora estamos trabalhando bastante para resolver este gargalo. Acredito que a solução para este problema também irá gera um post interessate para este blog.