Apache, processos, threads e django

Muitas vezes apenas adotamos os padrões e não nos questionamos porque eles foram escolhidos, então resolvi fazer uma breve pesquisa sobre threads e processos no Apache

Antes de mais nada, é importante falar um pouco sobre processos e threads. Os processos são independentes e têm seus próprios endereços de memória, os threads pertencem a um processo e compartilham o mesmo endereço de memória e recursos.

Tudo começa no webserver, vou falar do Apache. Existem dois sabores que se diferenciam em como será feito o processamento de múltiplas requisições: prefork e worker.

O prefork tem um processo de controle para outros sub-processos que irão receber e responder às requisições. Aqui não são utilizados threads, uma requisição é repassada para o primeiro sub-processo livre e este, por sua vez, só atenderá a uma requisição por vez.

O worker também tem um processo de controle para outros sub-processos, a diferença é que cada sub-processo cria um thread de escuta que fará o repasse de requisições para outros threads. Isto faz com que o uso de memória normalmente seja menor, pois os threads compartilham os mesmos recursos.

Em ambos os modelos é possível regular o mínimo ou máximo de processos e threads, o Apache se encarrega de reciclar os processos dentro destes limites. O ideal é que o servidor esteja configurado para aguentar o número esperado de requisições mas evitando consumir toda a RAM disponível, o que resultaria em uso da SWAP em disco e uma cascata de problemas.

Segundo a documentação, o prefork é indicado para evitar threading em aplicações que utilizam bibliotecas non-thread-safe, ou seja, evitar que ações em threads diferentes de um mesmo processo possam influenciar outro thread. Como o worker mantém um conjunto de threads em um mesmo processo, compartilhando recursos, bibliotecas mal preparadas podem causar resultados inesperados.

Em geral, o Django é tido como thread-safe, houve uma discussão sobre isso mas acredito que muitos dos problemas da época já tenham sido resolvidos. Ainda não fiz a experiência, mas este post do Armin Ronacher compara o sistema de templates do Django com o Jinja e comenta que algumas templatetags como a cycle não são thread-safe.

Por muito tempo o Django teve o modpython com Apache prefork como deploy recomendado, agora a recomendação oficial é para se utilizar o modwsgi* mas não cobre qual modelo de webserver é mais adequado.

* Curiosidade: conversando com o Jacob na Python Brasil aprendi que se pronuncia “mód uísgui”, mais fácil não? 🙂

Aqui vão mais alguns links interessantes:

Anúncios

Django UnicodeEncodeError com upload usando WSGI

Se você se deparou com um UnicodeEncodeError ao fazer o upload de algum arquivo com caracteres especiais usando WSGI no Django, a solução pode estar aqui: How to use django with mod_wsgi – Additional Tweaking.

Eu passei pelo seguinte erro:

UnicodeEncodeError: 'ascii' codec can't encode characters
in position 86-87: ordinal not in range(128)

Em resumo é preciso alterar as variáveis de ambiente LANG e LC_ALL do Apache:

export LANG='en_US.UTF-8'
export LC_ALL='en_US.UTF-8'

Isto é feito alterando o arquivo envvars do Apache, normalmente em /etc/apache2/envvars.

Em algumas instalações / distribuições, este arquivo pode não existir ou não estar sendo chamado no httpd.conf, neste caso é preciso configurar manualmente.

No FreeBSD é preciso criar um arquivo com a extensão .env em /usr/local/etc/apache2/envvars.d. Como isto é ligado ao WSGI, chamei de mod_wsgi.env.

Depois de alterar é preciso reiniciar o apache.

Agora uploads de arquivos nomeados com caracteres especiais funcionam normalmente.