Melhorando o GUJ: Jetty, NIO e load balancing

Postado em 27. jun, 2008 por Fabio Kung em Arquitetura, Caelum

GUJ2

Durante boa parte da vida do GUJ.com.br, na sua segunda versão (screenshot acima), o site sofreu diversas quedas e passou por muitos períodos de lentidão, mesmo depois de ter migrado para um servidor dedicado. A grande verdade é que por um bom tempo ficamos devendo a devida atenção ao deployment do GUJ. Sempre que um problema acontecia alguém simplesmente reiniciava o servidor, sem investigar as causas reais do problema com profundidade.

Dada a relação próxima que a Caelum sempre teve com o site, já que dois dos fundadores do GUJ são também os fundadores da Caelum, resolvemos assumir de vez a posição de criadores do GUJ. A Caelum agora é a patrocinadora e mantenedora oficial do GUJ. Recentemente andamos gastando algum tempo, tentando acabar de vez com estes problemas que o GUJ a tanto tempo sofre. Felizmente, a melhora já é bem perceptível!

Há algum tempo atrás, o GUJ ficava esporadicamente muito lento. Para resolver estes problemas de lentidão, o primeiro passo foi conseguir um servidor dedicado, pago pelos anúncios espalhados pelo site. Já faz tempo que o GUJ roda neste servidor dedicado e desde então a performance se tornou quase sempre aceitável.

Porém, o servidor dedicado não resolveu todos os problemas, já que java.lang.OutOfMemoryError sempre foi o principal problema enfrentado pelo GUJ. Sempre desconfiamos que o culpado poderia ser o código do próprio GUJ, ou até do JFórum, que deveriam conter algum vazamento de memória.

Alguns desenvolvedores da Caelum já tiveram ótimas experiências passadas com o Jetty, que é um excelente servidor web e servlet contêiner. Foi, inclusive, um dos servidores Java pioneiros a usar conectores NIO (java.nio). O Jetty foi desenhado para ser embutido em outras aplicações Java e portanto é extremamente leve. Consome bem menos memória que o Tomcat, seu concorrente mais conhecido, e não deixa nada a desejar nas outras características.

O servidor dedicado do GUJ tem 2GB de memória RAM e o Tomcat estava configurado inicialmente para ter um heap máximo de 768MB (-Xmx768M). A primeira tentativa foi aumentar o heap máximo (-Xmx1024M), mas mesmo assim o temível OutOfMemoryError insistia em aparecer.

Resolvemos então dar uma chance ao Jetty. Já que ele consome menos memória, acreditamos que os OutOfMemoryError demorariam mais a aparecer. Logo ao subir, o jetty ocupa 4% da memória do servidor. O impressionante é que em duas semanas no ar, o uso total de memória não passou de 12%. Na verdade, o uso de memória estabilizou em 9% do total, porém recentemente fizemos alguns testes de carga no servidor do guj, com mais do que o dobro do número de conexões que o guj recebe hoje nos períodos de pico. Isto fez com que o uso de memória do Jetty pulasse para 12%. Uma diferença enorme da quantidade usada pelo Tomcat, que chegava facilmente a 80%.

uso de memória do Jetty

Já temos algum tempo rodando, sem problemas de memória e com o jetty estável usando 9-12% do total da memória do servidor. Já estamos até pensando em descartar a possibilidade de existir um vazamento de memória no código do GUJ ou do JForum. Isto tornaria o Tomcat culpado pelos problemas de memória!

A possibilidade de vazamentos de memória no Tomcat (estávamos com a versão 6.0.14, sem o APR e com Linux Kernel 2.6.22) deste tamanho é um pouco assustadora. Com a base de usuários que o Tomcat tem, muito possivelmente alguém já teria pego este problema muito antes de nós. Provavelmente o problema é de alguma configuração mal feita no Tomcat do GUJ.

Fato é que o Jetty foi uma tentativa que deu certo e o problema está aparentemente resolvido. O Jetty mostrou um uso mais alto de CPU que o Tomcat, chegando a picos de 60% da capacidade total de processamento do servidor, que possui 2 processadores. Através dos testes de carga que fizemos, temos percebido que o Jetty usa bastante CPU para responder diversas requisições simultâneas. Isso não chega a ser um problema, já que os dois processadores que o servidor possui são mais do que suficientes para atender a quantidade de requisições por segundo que o GUJ recebe hoje.

Apesar de termos algumas suspeitas, ainda não investigamos a razão do alto uso de CPU. Este é um ponto a ser abordado, caso o GUJ tenha problemas com disponibilidade de processamento algum dia.

Mesmo não representando um problema hoje, esta situação preocupa já que o MySQL rodando na mesma máquina também costuma usar bastante CPU. Isto pode se tornar um problema em algum dia que tenha um pouco mais de requisições por segundo do que o comum, já que o uso de CPU chegava as vezes perto do limite. Felizmente, grande parte das requisições ao servidor do GUJ são para conteúdo estático: imagens, JavaScripts, arquivos CSS, download de pdfs (dos artigos), entre outros. Todo esse conteúdo estático era servido pelo Jetty, contribuindo para o alto uso de CPU. Resolvemos então tentar um proxy reverso na frente do Jetty, especificamente para servir este conteúdo estático.

proxy reverso servindo conteúdo estático

Existem diversas alternativas de proxy reverso e a primeira a ser considerada quase sempre é o conhecido servidor web Apache Httpd, com a adição do mod_proxy. É uma excelente solução e existe bastante documentação para fazer tudo funcionar. No entanto, faz um tempo que eu já estava querendo testar o servidor russo Nginx, tão falado pelo pessoal da Engine Yard.

A desvantagem do Nginx para o Apache Httpd é a documentação não tão extensa. Esse é um grande problema para a comunidade do Nginx, que tem se esforçado em traduzir grande parte do que está escrito em russo. Apesar disso, o Nginx é impressionante e superou todas as nossas expectativas. Além de ser extremamente rápido, consome pouquíssimos recursos. Cada um dos processos (1 master + 5 workers) consome na maior parte do tempo apenas 1% de CPU e 0.2% da memória disponível do nosso servidor. Incrível!

O conteúdo estático do GUJ agora é todo servido pelo Nginx; as requisições nem chegam ao Jetty. Além disso, o Nginx, como um bom proxy reverso, oferece diversas otimizações. Uma essencial para o GUJ é o preenchimento automático dos cabeçalhos HTTP de cache, sugerindo aos browsers que façam cache do conteúdo estático. Agora o consumo de CPU diminuiu consideravalmente.

uso de recursos do nginx no GUJ

Não fizemos nenhum comparativo científico de performance, mas a melhora dos tempos de resposta do GUJ está visível. Frequentemente tenho a sensação de que o site está “voando”.

Configuramos também no Nginx a exibição de algumas estatísticas de acesso; tão valiosas ao GUJ. É assustador como o padrão de acesso se repete a cada dia, e a cada semana. Repare como o gráfico de requisições por segundo é praticamente idêntico para cada dia (com exceção do sábado e domingo, que são parecidos entre si).

requisições por segundo no GUJ, em uma semana

Temos também o interesante gráfico de conexões por segundo, que mostra relação média aproximada de 3 requisições por conexão.

conexões por segundo no GUJ, em uma semana

Note que a legenda do gráfico está errada, já que deveria mostrar “connections/sec”. O pequeno pico que dá para ver no gráfico aparece por causa de alguns testes de carga que fizemos neste dia. Pode ser desconsiderado.

Fizemos ainda algumas experiências com múltiplos servidores Jetty por trás do Nginx, funcionando também como balanceador de carga. Espero em breve postar sobre nossa experiência em geral com balanceamento de carga e alguns outros truques que pudemos testar nesta experiência do GUJ e em alguns clientes.

Aproveitando o post, o GUJ recentemente comemorou a marca de meio milhão de mensagens. É um orgulho poder fazer parte desta comunidade, e mais ainda por poder torná-la cada vez melhor.

Fabio Kung

Tags: , , , , , , , , ,

28 Respostas para “Melhorando o GUJ: Jetty, NIO e load balancing”

  1. Dyego Souza do carmo

    27. jun, 2008

    Parabéns !
    Finalmente alguem mostrou-se preocupado com o GUJ…

    Poderiam dar a mesma dica pro pessoal do infoblogs né ? HEHEHE

    Falow !

  2. Diego Carrion

    27. jun, 2008

    Fábio, não utilizaram alguma ferramenta de profiling para tentar identificar o problema?

  3. Emerson Macedo

    27. jun, 2008

    Segundo os gráficos, as ações estão sem uma tendência definida :)

    Parabéns pela otimização. Também tenho notado melhora significativa na velocidade do site.

    []s

  4. Anderson Carubelli

    27. jun, 2008

    Muito Legal a preocupação com o GUJ, mas é excelente mostrar como foi (ou está sendo) feito para resolver os problemas, compartilhar a informação é o que torna a comunidade cada vez forte!

    parabens ao Fabio Kung e a equipe da Caelum!

    []´s
    Anderson

  5. Élcio leite

    27. jun, 2008

    Parabéns Fábio pela qualidade técnica do post e a todos que tem
    colaborado com o tão importante e salvador GUJ.

  6. Paulo Silveira

    27. jun, 2008

    Oi Diego! O Rafael Steil sempre roda profilers no JForum, e nao encontrou nada na ultima versao. Nao passamos o profiler no guj em si porque o site é muito simples e acabamos indo direto para o jetty, já que o Kung é fã de carteirinha.

    Se fosse um leak do site do guj em si, ja teria aparecido em 2 semanas de uso massivo….

  7. julio khichfy

    27. jun, 2008

    Muito bom!
    O GUJ sempre me salva e sempre salvará :)
    e o legal é que as vezes eu tb salvo
    o ciclo esta formado
    parabens

  8. Igor Costa

    28. jun, 2008

    Impressionantes essas estatisticas! Obrigado por compartilhar!

    Seria muito interessante um post sobre Nginx para conhecermos ele melhor.

  9. Ezequiel

    28. jun, 2008

    Fábio, você não postaria as configurações necessárias no nginx para rodar como servidor de conteúdo estático? Eu já utilizo o Jetty e gostaria de colocar o nginx como servidor de conteúdo.

    grato.

  10. Fabio Kung

    30. jun, 2008

    Oi Ezequiel,

    Ótima idéia para um próximo post! Posto sim. Quero falar mais um pouco ainda sobre o balanceamento de carga e nesse próximo post posto as configurações.

    Obrigado a todos pelos comentários!

  11. elomarns

    01. jul, 2008

    Belo post, e também belo complemento a sua palestra no Rio JUG. Há tempos escuto falarem bem do nginx, e, pelo conteúdo do post, parece que ele é mesmo merecedor dos comentários positivos sobre ele.

    P.S.: Em relação a palestra do Rio JUG, os slides dela foram ou serão disponibilizados?

  12. Fabio Kung

    04. jul, 2008

    @elomarns
    Tem razão. Ainda estou devendo mesmo um post sobre a palestra no RioJUG/FalandoEmJava. Tá no topo da lista de TODOs.

  13. eduveks

    18. jul, 2008

    Parabéns Fabio! Uma excelente aposta, e baseado nisto no q falamos no forum apostei nisto também, e configurei num servidor novo o Nginx e o Jetty, ficou excelente!

    Valew ;)

  14. Rafael Ponte

    05. ago, 2008

    Ótimo post :) Parabéns!

    Só queria dizer que os links para as imagens estão quebrados :( Se puderem consertar eu ficaria grato!

    Valeu!

  15. Aldivone

    10. out, 2008

    Onde posso pegar um plugin legal pro eclipse europa para usar o jetty?

  16. Fabio Kung

    16. out, 2008

    @Aldivone

    O próprio ato tem suporte para o jetty, mas precisa ser instalado separado. Na tela em que adicionam servidores, há uma opção para instalar outros.

  17. Vanilton Coelho

    07. abr, 2009

    E quando se utiliza o jboss como servlet conteiner, por causa da utilização de EJB, quais seriam as opções para este cenário?

  18. Antônio

    11. ago, 2009

    Fabio, como foi resolvido o problema de session id, tendo mais de um Jetty e load balanced, quando um jetty apresenta problema ou quando é feito nova liberação de versão, como é mantido a sessão do usuário que já está logado no Site.

    Obrigado

  19. Diego Lovison

    29. mar, 2011

Trackbacks/Pingbacks

  1. blog.caelum.com.br » Vazamento de memória e de conexões - setembro 2, 2008

    [...] Foram várias tentativas com profiling remoto offline, o código java invocava a api de profiling para depois analisarmos os resultados em nossas máquinas, porém um bug do JProfiler com o Apache Tomcat 6.0.16 inviabilizou tal análise. Fomos então rodar a aplicação no Jetty 7 (apontando para a mesma configuração do tomcat) sem que nenhum usuário da aplicação live notasse a mudança: uma migração transparente de servlet containers executada com êxito, assim como ocorreu no GUJ. [...]

  2. Diego Plentz » Blog Archive » Tomcat is deprecated - outubro 3, 2008

    [...] tempo que escutava o pessoal da Caelum (e principalmente o Kung) falando bem do Jetty, mas só quando se começa a usar que se percebe as [...]

  3. Tomcat: Rest in Peace? - CMilfont Tech - outubro 10, 2008

    [...] Kung publicou no blog da Caelum sobre os problemas enfrentados pelo GUJ (maior e melhor fórum sobre Java e arquitetura de software do Brasil) de quedas e lentidão. Há [...]

  4. Servlet Container Jetty 7.0 disponível para download - Gabriel Cardelli - outubro 10, 2009

    [...] Vale lembrar que o próprio GUJ.com.br migrou para o Jetty na época que estávamos enfrentando frequentes OutOfMemory e falhas de conexão, ao termos passado os 2 milhões de visitas por mês. Há até um relato sobre o assunto: http://blog.caelum.com.br/2008/06/27/melhorando-o-…uj-jetty-nio-e-load-balancing/ [...]

  5. Integrando IIS ao Jetty, Apache URL Redirect e Filtro ISAPI « Diego Chohfi - novembro 4, 2009

    [...] muita dor de cabeça. O pessoal da Caelum já chegou a relatar um problema parecido e documentado aqui com o deploy do GUJ e recomendo que leiam. Vamos então mostrar como configurar o IIS com o IAPI [...]

  6. Corrigindo erros após instalação do Nginx no Mac OS X « Portfólio do Mourdok - dezembro 22, 2009

    [...] os motivos em adotar esse servidor foram de alguns artigos da comunidade, como esse da Caelum (por Fábio Kung) e esse daqui também da Boox-box (por Marco Gomes), comentando sobre a baixa [...]

  7. Integração contínua: deploys e aprovações sem dor de cabeça para o cliente | blog.caelum.com.br - janeiro 18, 2010

    [...] efetua o deploy para produção. Nesse instante, os sistemas são restartados, de preferência com o uso de load balancers que permitem o restart sem a queda da aplicação, e o cliente está pronto para usar as [...]

  8. Simples Deploy de Aplicação JRubyOnRails com Warbler « Portfólio do Mourdok - abril 2, 2010

    [...] Esse que pode ser recomendado para utilização em sistemas, digamos: Singelo. Pois até hoje não vi o Servidor Tomcat, se sair bem quando a questão é desempenho. [...]

  9. 5 dicas simples de escalabilidade com Ruby | blog.caelum.com.br - dezembro 8, 2010

    [...] Configure o Apache ou o Nginx para servir conteúdo [...]

Deixar uma Resposta